Web-basierte Anwendungen - Medieninformatik (B.Sc.)

Web-basierte Anwendungen
Grundlagen und Frameworks
Prof. Dr. Peter Barth
Hochschule RheinMain
Fachbereich Design Informatik Medien
Medieninformatik
6. Juli 2015
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
1 / 295
Organisatorisches
Organisatorisches
Vorlesung
• Mittwochs, Raum 14, 8:15 – 9:45 Uhr
Praktikum
• Mittwochs, Raum 13: B ab 10:00 Uhr, A ab 14:15 Uhr, C ab 16:00 Uhr
Vorlesungsfolien, Übungsblätter, weitere Informationen
• https://read.mi.hs-rm.de
• http://www.mi.hs-rm.de/˜barth/hsrm/webanw
• /opt/share/praktika/WebAnw
Bewertung
• Prüfungsleistung, Mündliche Prüfung
• Studienleistung, Praktikum
70 %
30 %
• Projekt Web Python/Basis, mindestens 8 von 15 Punkten
• Projekt Web Servlet/Framework (Abgabe August), mindestens 8 von 15 Punkten
Infrastruktur
• Pool und/oder fertiges Image /opt/share/downloads/linux/ZuHause
• Jeder individuell ein Repository, 2015webanw/2015webanw<user>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
2 / 295
Organisatorisches
Ziele der Veranstaltung
Design und Realisierung Web-basierter Anwendungen
• Einsatzgebiete Web-basierter Anwendungen erkennen
• Problemadäquater Entwurf, Architektur und Technologie- Frameworkwahl
• Integration externer Dienste und Anwendungen, z.B. DBMS
• Sicherheitsaspekte, Lastaspekte
Umgang mit praxisrelevanten Web-Technologien
• Grundlagen (CGI, Templating, Integration): mit Python
Bewußt ohne (minimal) weitere Frameworks (selber machen)
• Applikationsserver/Servlet-Container: mit Tomcat
• Webkomponentensystem: mit Java Server Faces
Vorbereitung Praxisphase und Beruf
• Oft wichtigstes Thema
• Im Praktikum dann meist auch Python (Django), Ruby (Rails), PHP (Zend, Simfony,
Typo3), . . .
• . . . womit Sie nach Veranstaltung nach 1 Woche produktiv arbeiten können
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
3 / 295
Organisatorisches
Voraussetzungen
Auszeichnungssprachen
• HTML Grundkenntnisse
(de.selfhtml.org)
• XML Grundkenntnisse
Programmieren
• Python, Java
• OO, UI-Patterns
Datenbanken
• SQL
• Integration in
Programmiersprachen
Spaß
• An komplexen Dingen
Infos, Tools und Downloads:
/opt/share/praktika/WebAnw/
/opt/share/downloads/linux/ZuHause
• An der Umsetzung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
4 / 295
Organisatorisches
Literatur
CGI, Grundlagen
• http://tools.ietf.org/html/rfc3875
http://docs.python.org/2/library/cgi.html
• http://www.python.org/dev/peps/pep-0333,
http://www.modwsgi.org/
• Programming Python, O’Reilly, Lutz, Kapitel 16
Servlets
• Core Servlets and JavaServer Pages, Hall &
Brown, http://pdf.coreservlets.com/
• JAVAEE/servlet, JavaEE/jsp
http://jcp.org/en/jsr/detail?id=340
http://download.oracle.com/otndocs/jcp/jsp-2.2-mrel-oth-JSpec
Java Server Faces
• Java Server Faces 2.2, dpunkt, Marinscheck et. al
• JAVAEE/javaserverfaces-139869.html,
http://jcp.org/en/jsr/detail?id=344
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
5 / 295
Grundlagen Web
Einführung
Einführung
Web-basierte Anwendung
• Dokumentenbasiert, Client-Server
Client
• Präsentation, Dokumentendarstellung
• Web-Browser
• Desktop, mobiles Device
Server
• Anwendungsfunktionalität,
Dokumentenerzeugung
Anfrage
Antwort
• Web-Server
• Meist Server
Klassifikation
Server
• Inhalte, Layout, Interaktion
• Anzahl Nutzer, Zugriffe
• Sicherheit, Verfügbarkeit
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
6 / 295
Grundlagen Web
Einführung
Statische Inhalte
Historie
• 88, Tim Berners-Lee, Enquire
• Hypertext,
<a href="..." >WWW</a>
• 90, Name „World Wide Web“
• Standards HTTP, URI, HTML
HTTP-Client (Web Browser)
Übertragungsprotokoll
• HTTP, Hypertext Transfer Protocol
Eindeutiger Zugriff auf Ressourcen
www.html
Anfrage
• URI (Bezeichner), Uniform Resource
Identifier
Antwort
http://de.selfhtml.org/intro/
internet/www.htm
de.selfhtml.org
Http-Server (Web Server)
• URL (Locator), ein URI
Konfiguration
Dokumentenformat
www.htm
• HTML, Hypertext Markup Language
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
7 / 295
Grundlagen Web
Einführung
Dynamische Inhalte
Statische Dokumente
• Dokument liegt vorbereitet vor
• Keine Personalisierung, keine
zustandsabhängigen Seiten, keine
Interaktion
Dokumente dynamisch generieren
HTTP-Client (Web Browser)
• Erzeugen des Dokuments für die
„dynamisch
generiert“
Antwort der Anfrage
• Durch Ausführen eines Programms
unter Berücksichtigung des Zustands
Antwort
Anfrage
https://www.mi.hs-rm.de/portal/
• Programm-Ausgabe ist HTML
www.mi.hs-rm.de
Client/Server
Http-Server (Web Server)
• Web-Server weiß wann was generiert
Konfiguration
wird und kommuniziert mit Programm
• Web-Client (Browser) merkt davon
nichts
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
8 / 295
Grundlagen Web
Einführung
Dynamische Inhalte – Server
Common Gateway Interface (CGI)
Host
HTTP-Server
• Einfache Integration, mit allen
Programmen und Umgebungen
möglich, meist Skriptsprachen
Prozess
• Aufruf eines externen Programms
(separater Prozess) je Anfrage
• Kommunikation über stdin/stdout und
Host
HTTP-Server
Umgebungsvariablen
Integration in Web-Server
Prozess
• Programm und Ablaufumgebung
(Interpreter) läuft im Web-Server
• Effizienter, ein Prozess für mehrere
Anfragen
HTTP-Server
HTTP-Server
Host
Container
Separater Container
• Eng integriert mit HTTP-Server oder
eigener HTTP-Server
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
9 / 295
Grundlagen Web
Einführung
Infrastruktur an der Hochschule
Host: www.mi.hs-rm.de
Web-Server: Apache, httpd
• ˜ssinn001/public_html/
• Verfügbar als http.../˜ssinn001/
CGI-Programme
• ˜ssinn001/public_html/cgi-bin/p.cgi
httpd (apache2)
• Verfügbar als CGI-Programm
http.../˜ssinn001/cgi-bin/p.cgi
www
login3
mi1x
Prozess
• Nur in public_html/cgi-bin
Integration in Web-Server, Python
• Web Server Gateway Interface (WSGI)
httpd (apache2)
• Unter Apache Benutzerkennung
• ˜ssinn001/public_html/wsgi/app.wsgi
www
login3
mi1x
Prozess
Prozess
http.../˜ssinn001/wsgi/app.wsgi
• Eine WSGI-Datei je App
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
10 / 295
Grundlagen Web
Einführung
Dynamische Inhalte – Client
Roundtrips Visualisierung
• Problem: Jede Interaktion bedingt
Server-Kommunikation, auch reine
Visualisierung
• Lösung
• Skript (Javascript) im Web-Browser
• Veränderung der Anzeige durch
Beispiel
Autocompletion
HTML
Manipulation des DOM
XML(Daten)
HTTP-Server
Roundtrips Flackern, wenig Daten
• Problem: Neuaufbau Seite bei
Server-Zugriff (Usability), vorhandene
Daten neu übertragen
• Lösung
• Skript (Javascript) im Web-Browser
• Geänderte Daten nachladen,
Aktualisieren über DOM-Manipulation
• Daten asynchron, HTTP/WebSocket
Prof. Dr. Peter Barth (HS-RheinMain)
Asynchronuous Javascript and XML
(AJAX)
• In Veranstaltung nur Verwendung,
nicht wie es funktioniert
• Vertiefung im 5. Semester
Web-basierte Anwendungen
6. Juli 2015
11 / 295
Grundlagen Web
Common Gateway Interface
Erzeugen dynamischer Web-Seiten auf dem Server – CGI
Common Gateway Interface (CGI)
• Starten externer Programme zur
Dokumentenerzeugung
• Kommunikation über
Umgebungsvariablen und
Standardeingabe / Standardausgabe
• Standardisiert
Antwort
Vorteil
• Funktioniert in jeder Umgebung, mit
Anfrage
jeder Programmiersprache
• Darauf aufbauend alles realisierbar
Nachteil
• Ressourcen-intensiv, ein neuer
HTTP-Server
Standardausgabe
Umgebungsvariablen
Prozess je Anfrage
Host
Prozess
• Keine weitergehenden Features
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
12 / 295
Grundlagen Web
Common Gateway Interface
CGI-Programm in Python
Ausgabe auf Standardausgabe
• Erst Header, dann Content
(HTTP-Protokoll)
• Getrennt durch eine Leerzeile
(Extra newline Zeile 2)
HTTP-Header
• Content-Type zwingend notwendig,
meist text/html
• Weitere Header-Zeilen möglich,
hallo.cgi
1
werden meist von Web-Server ergänzt
2
3
HTML-Dokument, Content
4
5
• Einfach print auf Standardausgabe
6
7
#!/usr/bin/python
print("Content-Type: text/html\n")
print("<html>")
print("<head><title>Hallo</title></head>")
print("<body>")
print("Hallo CGI Python")
print("</body></html>")
• Beliebiger Code
Fehler: „Internal Server Error“
• Vielleicht noch was im Log...
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
13 / 295
Grundlagen Web
Common Gateway Interface
CGI Beispiel – Details
Antwort
Anfrage
GET
2 /~pbart001/cgi-bin/hallo.cgi
3 HTTP/1.1
1
1
2
3
4
Umgebungsvariablen
1
2
3
4
5
6
7
8
9
10
11
12
SERVER_SOFTWARE=Apache/2.2.14 ....
SCRIPT_NAME=/~pbart001/cgi-bin/hallo.cgi
REQUEST_METHOD=GET
SERVER_PROTOCOL=HTTP/1.1
HTTP_CONNECTION=Keep-Alive
REMOTE_ADDR=172.26.33.61
SERVER_PORT=80
SERVER_ADDR=195.72.105.32
DOCUMENT_ROOT=/var/www/
HTTP_HOST=www
GATEWAY_INTERFACE=CGI/1.1
REMOTE_PORT=53197
Prof. Dr. Peter Barth (HS-RheinMain)
5
6
7
8
9
HTTP/1.1 200 OK
Date: Mon, 04 Feb 2013 10:21:42 GMT
Content-Encoding: gzip
Connection: Keep-Alive
Content-Length: 82
Server: Apache/2.2.14 (Ubuntu) ...
Vary: Accept-Encoding
Content-Type: text/html
Keep-Alive: timeout=15, max=100
10
11
<html>...
Standardausgabe
1
Content-Type: text/html
2
<html>
<head><title>Hallo</title></head>
5 <body>
6 Hallo CGI Python
7 </body></html>
3
4
Web-basierte Anwendungen
6. Juli 2015
14 / 295
Grundlagen Web
Common Gateway Interface
Komfortable CGI-Programmierung
Python-Bibliothek
• cgi Hilfsroutinen
• cgitb (TraceBack), Ansicht
Fehlermeldungen, sehr sinnvoll
hallokomf.cgi
#!/usr/bin/python
# immer gleich am Anfang
3 print("Content-Type: text/html\n")
4 import cgi, cgitb
5 cgitb.enable()
1
2
6
7
8
9
10
11
12
13
# ab hier spezifisch je Skript
fehler
print("<html>")
print("<head><title>Hallo</title></head>")
print("<body>")
print("Hallo CGI Python")
print("</body></html>")
Prof. Dr. Peter Barth (HS-RheinMain)
Fehler in Zeile 8 wird erkannt und
entsprechend im Browser ausgegeben.
Sehr hilfreich beim Entwickeln.
Web-basierte Anwendungen
6. Juli 2015
15 / 295
Grundlagen Web
Common Gateway Interface
Umgebungsvariablen
Informationen in Umgebungsvariablen
• Vom Web-Server, aus Anfrage
• Vom Skript verwertbar
Informationen – Anfrage
• Anfragemethode, Query-String, . . .
Informationen – Infrastruktur
• Web-Browser, Web-Server, . . .
env.cgi
1
2
3
4
5
6
7
8
#!/usr/bin/python
print("Content-Type: text/html\n")
import cgi, cgitb
cgitb.enable()
import os
for key in os.environ:
print("%s=%s" % (key, os.environ[key]))
print("<br />")
Prof. Dr. Peter Barth (HS-RheinMain)
Beispiel ohne HTML-Rahmen. Viele Browser können daraus immer noch
etwas machen. Für Vorlesung ok, da man ansonsten zu viel Boilerplate-Code hat.
Sie generieren immer korrektes HTML.
Web-basierte Anwendungen
6. Juli 2015
16 / 295
Grundlagen Web
Common Gateway Interface
Anfragemethode GET
Uniform Resource Locator (URL)
• Abruf einer Ressource
• Beispiel-URL
http://www/˜pbart001/cgi-bin/hallo.cgi
• Protokoll: HTTP
• Server: www(.mi.hs-rm.de)
• Pfad: /˜pbart001/cgi-bin/hallo.cgi
HTTP-Protokoll GET
Antwort
Anfrage
• GET /˜pbart001/cgi-bin/hallo.cgi HTTP/1.1
• Methode GET
GET
~pbart001/cgi-bin/hallo.cgi
HTTP/1.1
Host
HTTP-Server
• Ressource /˜pbart001/cgi-bin/hallo.cgi
Prozess
• HTTP Protokoll Version 1.1
REQUEST_METHOD = GET
SERVER_PROTOCOL = HTTP/1.1
REQUEST_URI = /~pbart001/cgi-bin/hallo.cgi
Im Web-Server verpacken
• In Umgebungsvariablen
• POST Inhalte in stdin
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
17 / 295
Grundlagen Web
Common Gateway Interface
Anfragemethode GET – Query-String
Ziel
Trennendes
&
• Übergabe von zusätzlichen
Informationen an Web-Server
• Nur die Anfrage (Query) betreffend
Query-String – Anfrageparameter
Einleitendes
?
• String an die URL angehängt
?name=Tim&ende=ciao
• Einleitendes ?
• Schlüssel/Wert-Paare durch & getrennt
• Kodierung: ohne Änderung
[a-zA-Z0-9]|’.’|’-’|’˜’|’_’,
Leerzeichen als +, %FF HEX sonst
Schlüssel
name
ende
Wert
=
=
Tim
ciao
Query-String Integration bei CGI
• Durch Umgebungsvariable
• QUERY_STRING
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
18 / 295
Grundlagen Web
Common Gateway Interface
Query-String verwenden
Ohne Query-String
CGI-Bibliothek
• Auslesen aus FieldStorage
• Vorgefertigtes Aufbereiten
querystring.cgi
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
print("Content-Type: text/html\n")
import cgi, cgitb
cgitb.enable()
import os
form = cgi.FieldStorage() # Aufbereiten
name, ende = "noname", "EOF"
if "name" in form:
name = form["name"].value
if "ende" in form:
ende = form["ende"].value
print("Hallo %s,<br />%s" % (name, ende))
print("<br />%s" % os.environ["QUERY_STRING"])
Prof. Dr. Peter Barth (HS-RheinMain)
Query-String ?name=Tim&ende=ciao
Web-basierte Anwendungen
6. Juli 2015
19 / 295
Grundlagen Web
Common Gateway Interface
Formulare erzeugen Query-String
querystringform.html
Erzeugen Query-String mit
<form>-Tag
1
2
3
• action-Attribut: (relative)
Ziel-URL
• method-Attribut:
4
5
6
7
<html><body>
<form action="querystring.cgi" method="get">
Name: <input type="text" name="name" /> <br />
Ende: <input type="text" name="ende" /> <br />
<input type="submit" name="ok" value="Ok" />
</form>
</body></html>
Anfrage-Methode, get (oder
post)
Eingabefelder mit <input>-Tag
• type-Attribut: Welches
Eingabefeld
• text: Text-Eingabefeld
• submit: Knopf
• . . . SelfHTML
Erzeugter Query-String im Beispiel
• name=Tim&ende=ciao&ok=Ok
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
20 / 295
Grundlagen Web
Common Gateway Interface
Parameter mit POST statt GET
POST statt GET
Änderung in HTML-<form>, nicht in
FieldStorage (ist für POST und GET)
Weitergabe CGI über Standardeingabe
• Philosophie: Daten schicken statt
holen
• Server-Zustand darf sich ändern
1
2
<form action="postparam.cgi"
method="post">
• Wiederholte Anfragen können
unterschiedliche Ergebnisse haben,
Ergebnisse nicht cachen
Eigenschaften POST
• Verpacken auch in String aus
Schlüssel/Wert-Paaren, aber
• Größere Datenmengen möglich (GET
auf 2 KByte beschränkt), auch
Binärdaten (Bilder, etc.)
• Nicht sichtbar in URL (nicht im
postparam.cgi
#!/usr/bin/python
2 print("Content-Type: text/html\n")
3 import sys
4 print sys.stdin.read()
1
Query-String)
• Meist nicht sichtbar in Log-Datei
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
21 / 295
Grundlagen Web
Formularelemente
Formularelemente – Hausaufgabe
Wichtigste Formularelemente
• Einzeiliges Eingabefeld
• Einzeiliges Eingabefeld für Passwörter
• Mehrzeiliges Eingabefeld
• Mehrzeilige Auswahlliste
• Dropdown-Box, einzeilige Auswahlliste
• Radiobuttons
• Check Box
• Versteckte Eingabewerte
• Aktion ausführen, Knopf/Button
In SelfHTML nachlesen und ausprobieren
• Namen/Werte setzen, vorbelegen,
formatieren
• http://de.selfhtml.org
/html/formulare/index.htm
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
22 / 295
Grundlagen Web
Formularelemente
Formulare und Mehrfachauswahl
mehrfach.cgi
Mehrfachauswahl
#!/usr/bin/python
2 print("Content-Type: text/html\n")
3 import os, cgi
1
• Derselbe Schlüssel mehrfach in
Formular verwendet
4
• Bei Radiobuttons natürlicherweise
• Mehrere Ergebnisse in Parameter je
form = cgi.FieldStorage()
for skill in form.getlist("skill"):
7
print("Ich kann %s <br />\n" % skill.capitalize())
5
6
Schlüssel möglich
• Muss in Anwendungsprogramm
berücksichtigt werden
1
2
3
4
5
6
7
8
<html><body>
<form action="mehrfach.cgi" method="post">
<input type="checkbox" name="skill" value="c"/> C <br />
<input type="checkbox" name="skill" value="java"/> Java <br />
<input type="checkbox" name="skill" value="python"/> Python <br />
<input type="submit" name="ok" value="Ok" />
</form>
</body></html>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
23 / 295
Grundlagen Web
Templating
Erzeugen der Dokumente
Dokumentenfragmente als String im Quellcode :-(
• HTML im Quellcode nicht als Markup zu erkennen
• Markup-Quelle ist nicht validierbar
• Keine Tool-Unterstützung beim Editieren
#!/usr/bin/python
ctype = "Content-Type: "
3 mime = "text/html"
4 print(ctype+mime+"\n")
1
2
5
6
• Vermischen von Anwendungslogik und Präsentation
7
8
Ziel
9
10
• Trennen von Anwendungslogik und
11
12
HTML-Präsentation
print("<html>")
print("<head><title>")
print("Hallo")
print("</title></head>")
print("<body>")
print("Hallo CGI Python")
print("</body></html>")
• HTML-Präsentation auslagerbar
• Anwendungslogik frei von Markup
Ansatz – Templating
• Server Pages Konzept
• Template-Engines
Achtung: Art der Dokumentenerstellung unabhängig von
technischer Integration (egal ob CGI oder proprietär)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
24 / 295
Grundlagen Web
Templating
Server Pages Konzept
hello.sp
Server Pages
1
2
• Einbettung von Skript-Code in
3
Markup (HTML)
4
5
• Zwischen speziellen Tags,
6
7
z.B. <? .. ?> oder <% ... %>
<!-- Server Page --!>
<html><body>
<?
s = "Hello World"
print(s)
?>
</body></html>
hello.sp.py
• Verfügbar für viele Sprachen und
#!/usr/bin/python
2 # Generated
3 print("Content-Type: text/html\n")
1
Umgebungen
Entwicklungssicht
4
• Erstelle statische HTML-Seite
print("<!-- Server Page --!>")
print("<html><body>")
7 s = "Hello World"
8 print(s)
9 print("</body></html>")
5
6
• Spezieller Bereich „agiert dynamisch“
Ausführungssicht
• Übersetze HTML-Seite in Skript
• HTML kopiert sich auf Ausgabe
• Code bleibt erhalten
• Führe entstehendes Skript aus
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
25 / 295
Grundlagen Web
Templating
Server Pages – Anwendungsgebiet
Anwendungsgebiet
• Präsentations-Templates, Fokus auf
Darstellung/HTML
• Behandelt Problem, dass Markup in
Zeichenketten nicht als solcher
erkennbar und validierbar ist
Vorteile
• Web-Designer (HTML-Autoren)
können Templates mit vorhandenen
Skills anpassen
• Kann mit HTML-Editoren bearbeitet
und teilweise validiert werden
(ignorieren Skriptteil)
• Code im HTML nicht besser als HTML
im Code
• Ersetzt durch dedizierte
Templating-Engines
• Schnell Ergebnisse
Nachteil: Fördert chaotische Mischung
von Präsentation und Anwendung
Prof. Dr. Peter Barth (HS-RheinMain)
Reinform obsolet:
• Heißt auch als Template verwendet
noch „*SP“
Web-basierte Anwendungen
6. Juli 2015
26 / 295
Grundlagen Web
Templating
Dokumentenerzeugung mit Templates
Trennen Markup und Code
• Markup als separate Teile, z.B.
Zeichenketten oder Dateien
Templates
• Markup mit speziellen Platzhaltern
Eingaben
Skript
• Code generiert Inhalte
• Am Ende Templates mit Inhalten füllen
Seite
und ausgeben
Beispiel
• Tabelle erzeugen mit
Klein-/Großbuchstaben
• Nächste Seite
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
27 / 295
Grundlagen Web
Templating
Dokumentenerzeugung mit Templates – Beispiel
Spaghetti-Code
Template-Code
spagzeichen.cgi
tempzeichen.cgi
#!/usr/bin/python
2 print("Content-Type: text/html\n")
3 import cgi, cgitb, string
4 cgitb.enable()
1
5
5
1
6
prog = "Zeichen"
7
print("<html><head><title>")
print(prog)
10 print("</title></head>")
11 print("<body><table>")
9
12
14
15
16
17
18
19
6
7
8
13
#!/usr/bin/python
2 print("Content-Type: text/html\n")
3 import cgi, cgitb, string
4 cgitb.enable()
8
9
10
11
12
for c in string.lowercase:
print("<tr><td>")
print(c)
print("</td>")
print("<td>")
print(c.upper())
print("</td></tr>")
page = """<html>
<head><title>%s</title></head>
<body>
<table>%s</table>
</body>
</html>"""
tabline="<tr><td>%s</td><td>%s</td></tr>"
13
lines = []
for c in string.lowercase:
16
lines.append(tabline%(c,c.upper()))
14
15
17
18
print(page % ("Zeichen", "\n".join(lines)))
20
21
print "</table></body></html>"
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
28 / 295
Grundlagen Web
Templating
Templating mit Python
tpage.tpl
Einfachst-Templating eingebaut
1
• Python Formatier-Operator %
2
• Ersetzungsstelle in template durch
4
%(name)s
3
markieren
6
7
• In Ersetzungs-Dictionary
dic["name"] = wert
5
<html>
<head><title>%(title)s</title></head>
<body>
<h1>%(title)s</h1>
<table>%(tablecontent)s</table>
</body>
</html>
tabline.tpl
setzen
• Für mehrere Stellen und mehrere
Name/Wert-Paare
<tr>
2
<td>%(first)s</td>
3
<td>%(second)s</td>
4 </tr>
1
• Einsetzen mit template % dic
Template-Engines
• Reichlich vorhanden, ein paar
Dutzende in
http://wiki.python.org/moin/Templating
• Mehr Features, zum Beispiel Iterieren
• Uns reicht Einfachst-Templating
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
29 / 295
Grundlagen Web
Templating
Templating mit Python – Beispiel
tplzeichen.cgi
Beispiel
• Template-Dateien vollständig einlesen
• Ersetze z.B. title durch Zeichen, zwei
Mal, im Header und am Anfang der
Seite
• Typische letzte Zeile, vorher nie eine
Ausgabe
#!/usr/bin/python
2 print("Content-Type: text/html\n")
3 import cgi, cgitb, string
4 cgitb.enable()
1
5
6
7
tabline = file("tabline.tpl").read()
page = file("tpage.tpl").read()
8
lines = []
for c in string.lowercase:
11
dic = {’first’ : c,
12
’second’ : c.upper()}
13
lines.append(tabline%dic)
9
10
14
15
16
dic = {’title’: "Zeichen"}
dic[’tablecontent’] = "\n".join(lines)
17
18
Prof. Dr. Peter Barth (HS-RheinMain)
print(page % dic)
Web-basierte Anwendungen
6. Juli 2015
30 / 295
Grundlagen Web
Templating
Sichere Integration von Inhalten
Korrekte/sichere Darstellung von Texten
• Spezielle Regeln für Markup (HTML)
müssen eingehalten werden (Aufgabe
des Template-Autors)
• Benutzerdefinierte Inhalte (potenziell
böse) müssen integriert werden
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
# -*- coding: utf-8 -*print("Content-Type: text/html\n")
import os, cgi
lines = ["Hallo", "Hällo", "<tag>", "<b> bold </b>",
’a " double quote’]
for line in lines:
print("%s <br />" % line)
print
for line in lines:
print("%s <br />" % cgi.escape(line, True))
Probleme – böse Inhalte
• Browserland ist Feindesland
• Texte mit Sonderzeichen/Tags
Vorgefertigte Funktionen verwenden
• Beispiel „Quotieren“ von Strings mit
cgi.escape,
Sonderzeichen und
Tag-Zeichen (<, >) umwandeln
• Einfügen von Skript-Code verhindert
Hallo <br />
Hällo <br />
<tag> <br />
<b> bold </b> <br />
a " double quote <br />
Hallo <br />
Hällo <br />
&lt;tag&gt; <br />
&lt;b&gt; bold &lt;/b&gt; <br />
a &quot; double quote <br />
• Weiteres: Entfernen von Tags, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
31 / 295
Grundlagen Web
Templating
Komponenten zur Ansicht
# Markup nur konstant, auslagerbar/aenderbar
_table = "<table>\n%(header)s\n%(data)s\n</table>\n"
3 _row = "<tr>%s</tr>"
4 _header = "<th>%s</th>"
5 _entry = "<td>%s</td>"
1
Rendern von HTML beschränken
2
• Manuelles Generieren von HTML
unabhängig von Präsentationslogik
6
• Reines Rendern (Generieren) von
7
8
HTML unabhängig, wiederverwendbar 9
10
realisieren
11
Beispiel – Tabelle
#!/usr/bin/python
print("Content-Type: text/html\n")
3 import cgi, cgitb, string
4 cgitb.enable()
1
2
from tabelle import table
9
10
11
12
13
14
15
14
15
16
18
19
7
8
13
17
5
6
12
20
def _rowformat(line, keys, wrap):
entries = [wrap % line[t[0]] for t in keys]
return _row % " ".join(entries)
def table(keys, data):
dic = dict()
hdict = {}
for tup in keys:
hdict[tup[0]] = tup[1]
dic["header"] = _rowformat(hdict, keys, _header)
rows = [_rowformat(line, keys, _entry)
for line in data]
dic["data"] = "\n".join(rows)
return _table % dic
V,N,M = ’vorname’,’nachname’,’matnr’
keys = [(V, ’Vorname’), (N, ’Nachname’), (M, ’MatNr’)]
data = [{V: ’Susi’, N: ’Sinnlos’, M: ’135789’},
{V: ’Rudi’, N: ’Ratlos’, M: ’222333’},
{V: ’WWW’, N: ’WickedWestWitch’, M: ’666666’}]
ctable = table(keys, data)
c = "<html><body><h3>Tabelle</h3>%s</body></html>" % ctable
print(c)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
32 / 295
Grundlagen Web
Proprietäre Integration
Vor- und Nachteile von CGI
Vorteile
Einsatzgebiet
• Einfach, standardisiert
• Einfache Anwendungen
• Funktioniert immer, mit jeder Programmiersprache
• Ausführung unter der eigenen Benutzerkennung
mit wenigen
dynamischen Seiten
• Darstellung und
Nachteile
Eingabe von
strukturierten Daten
• Je Anfrage ein Prozess, Last
• Lösungsansatz: FastCGI, mehrere Anfragen je
Prozess, vermeidet Start der Ablaufumgebung
• Lösungsansatz: Integration in Web-Server, meist
proprietär, Interpreter/Runtime läuft im Web-Server,
mehr Features möglich
• Keine Trennung Anwendungslogik und
Präsentationslogik erzwungen
• Einfache
Wiederverwendung von
vorhandenem
Anwendungs-Code
Beispiele
• Bugzilla, Bugtracking
• Verleitet zu Spaghetti-Code (muss aber nicht sein)
Tool, Perl
• MoinMoin, Wiki, Python
• ...
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
33 / 295
Grundlagen Web
Proprietäre Integration
Integration von Python in Web-Server – WSGI
Ziele
HTTP-Server
• Integration so einfach wie CGI
• Kein neuer Interpreter je Aufruf, ein
Prozess für mehrere Anfragen
WSGI
Python-Prozess
• Unabhängig vom Web-Framework
Web Server Gateway Interface (WSGI)
Python-Prozess
• Python-Schnittstelle zur Integration in
Web-Server, Teil der
Standardbibliothek
Apache-HTTP-Server
• Gut dokumentiert, PEP-0333 (2003)
• Akzeptiert: Von (fast) allen
Frameworks und HTTP-Servern
unterstützt www.wsgi.org
mod_wsgi
(WSGI-Gateway)
WSGI-Protokoll
Python-Prozess
mod_wsgi für Apache-Integration
• code.google.com/p/modwsgi/
• Proprietäre effiziente Integration
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
34 / 295
Grundlagen Web
Proprietäre Integration
Ablauf – WSGI im Web-Server
Anfrage von Client im Web-Server
• Skript jetzt.wsgi wird verwendet
• Warum konfigurierbar (kommt noch),
zum Beispiel anhand von Dateiname
Übergabe an Python Prozess/Interpreter
Apache-HTTP-Server
• Nur bei erstem Aufruf
mod_wsgi
(WSGI-Gateway)
• Python soll Modul jetzt importieren
• Python übersetzt Modul in Byte-Code
und lädt Modul
• Bei jedem Aufruf
Python-Prozess
• Konfigurierte Funktion wird gerufen
Importieren,1x
(zum Beispiel application)
• Iterieren über Funktionsergebnis ist
Ergebnisseite
Gut für Betrieb, aber bei Entwicklung
zunächst kein Neuladen bei Änderung
Prof. Dr. Peter Barth (HS-RheinMain)
Funktionsaufruf,
jedes Mal
Übersetzen,
1x
Python
Bytecode
Web-basierte Anwendungen
Python
Quellcode
6. Juli 2015
35 / 295
Grundlagen Web
Proprietäre Integration
WSGI
jetzt.wsgi
HTTP-Server mit WSGI liefert
1
• environ Umgebungsvariablen
2
• start_response Antwort-Funktion
4
3
5
6
HTTP-Server mit WSGI erwartet
7
• Eine Funktion
import datetime
page = "<html><body>%s</body></html>"
def app(environ, start_response):
today = str(datetime.datetime.today())
header = [("Content-Type","text/html")]
start_response("200 OK", header)
return [page % today]
8
9
application = app # mod_wsgi
• Params (environ, start_response)
• Aufruf von start_response mit
• status: HTTP Status, meist 200 OK
• header: Liste von Schlüssel/Wert-
Tupeln, zum Beispiel mit
("Content-Type", "text/html")
• Rückgabe ein iterierbares Objekt aus
WSGI und Apache
• mod_wsgi
dem Dokument erzeugt wird
Beispiel
• Erwartet den Namen application
• jetzt.wsgi, aktuelles Datum/Uhrzeit
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
36 / 295
Grundlagen Web
Proprietäre Integration
URL auf WSGI-Skript abbilden
Abbildung bei CGI/WSGI
• Konfiguration Web-Server
• Meist automatische Assoziation von
Skriptname im Pfad auf Skript
• Eingeschränkt auf spezifisches
Unterverzeichnis cgi-bin bzw. wsgi
unterhalb von public_html mit
Endung .cgi bzw. .wsgi
• Achtung: Pfadangabe nach
Skriptname möglich (und sinnvoll)
Im Produktivbetrieb nicht sichtbar
• Web-Server Konfiguration (z.B.
Apache mod_rewrite), alle Anfragen
auf ein Skript, sinnvoll
• Hochschule: Angabe von Skript
Prof. Dr. Peter Barth (HS-RheinMain)
http://www.mi.hs-rm.de/
˜ssinn001/wsgi/jetzt.wsgi
Web-basierte Anwendungen
6. Juli 2015
37 / 295
Grundlagen Web
Proprietäre Integration
Je nach URL andere Seiten erzeugen
url.wsgi
URL-Mapping
1
2
• Üblich bei statischen Inhalten:
3
Assoziation zwischen Dateipfad und
Zugriffspfad in URL
4
5
6
def app(environ, start_response):
rurl = environ["PATH_INFO"]
page = "Rest-URL: %s" % rurl
header = [("Content-Type","text/html")]
start_response("200 OK", header)
return [page]
7
• Assoziation abhängig von der
8
application = app # mod_wsgi
Konfiguration des HTTP-Servers
• Bei Anwendungen meist alles, bis auf
statische Dateien, auf ein Skript
• (Rest-)URL kann verwendet werden,
um andere Seiten zu generieren
Konfiguration an Hochschule
• Skriptname unterhalb von
public_html/wsgi mit Endung .wsgi
• Alles „darunter“ auch! URL darf weiter
gehen
Bei cgi gleiches Verhalten
Mapping: Je nach URL andere Funktion
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
38 / 295
Grundlagen Web
Proprietäre Integration
GET Anfragen mit WSGI
get.wsgi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET Anfragen mit WSGI
• Zugriff auf Query-String wie bei cgi,
parse_qs
auch bei cgi
Rückrichtung mit urllib.urlencode
• Folge von Schlüssel/Wert-Paaren als
• parse_qs liefert Dict. aus Inhalten des
Query-Strings, Achtung: Liste als
Wert, da mehrere Einträge erlaubt sind
• Abhängig von Query-String Inhalten
unterschiedliche Seiten generieren
Prof. Dr. Peter Barth (HS-RheinMain)
from urlparse import parse_qs
def application(environ, start_response):
query = parse_qs(environ[’QUERY_STRING’])
content = "Bye"
if "hi" in query:
content = hi(query)
header = [("Content-Type","text/html")]
start_response("200 OK", header)
return [content]
def hi(query):
name = "niemand"
if "name" in query:
name = "".join(query["name"])
return "Hallo, %s\n" % name
ein Query-String kodieren
• Tupel können Sonderzeichen
enthalten, die encodiert werden
• Leerzeichen als +, & als %26
• %XY generische Kodierung
Web-basierte Anwendungen
6. Juli 2015
39 / 295
Grundlagen Web
Proprietäre Integration
POST-Anfragen mit WSGI
postparam.wsgi
1
from urlparse import parse_qs
2
3
4
5
6
7
8
9
10
11
12
POST Anfragen mit WSGI
13
14
• Änderung Formular für POST
15
16
<form action="postparam.wsgi"
17
method="post">
18
def app(environ, start_response):
name, ende = ["niemand"], ["None"]
post = ""
if environ[’REQUEST_METHOD’] == ’POST’:
contentlen = int(environ[’CONTENT_LENGTH’])
post = environ[’wsgi.input’].read(contentlen)
dic = parse_qs(post)
if "name" in dic:
name = dic["name"]
if "ende" in dic:
ende = dic["ende"]
tup = (name[0], ende[0])
content = "Hallo %s, <br />%s" % tup
header = [("Content-Type","text/html")]
start_response("200 OK", header)
return [content]
19
• Zugriff auf Query-String durch Lesen
20
application = app # mod_wsgi
des Eingabestroms
• Nur so viel lesen wie zu dieser
Anfrage gehört
• Verarbeiten wie bei GET mit parse_qs
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
40 / 295
Grundlagen Web
Proprietäre Integration
Lokale WSGI-Umgebung zur Entwicklung
envwsgiref.wsgi
Entwicklung
• Integration in HTTP-Server
(Container) kompliziert und für
Entwicklung nicht notwendig
• Einfacher HTTP-Server Teil der
Python Standardbibliothek
• Nutzung mit Paket wsgiref
Paket wsgiref
• Referenzimpl. von wsgi
• simpleserver für Entwicklung
• make_server erstellt
HTTP-Server mit
WSGI-Anwendung
1
#!/usr/bin/python
2
def app(env, start_response):
header = [("Content-Type","text/html")]
5
content = ["%s: %s <br />" % (k,env[k]) for k in env]
6
start_response("200 OK", header)
7
return content
3
4
8
9
application=app
10
if __name__ == ’__main__’:
from wsgiref.simple_server import make_server
13
httpd = make_server(’localhost’, 8080, app)
14
httpd.serve_forever()
11
12
mod_wsgi
Kompatibilität
• app in application umbenennen
• if: Anwendung mit mod_wsgi im Apache und
wsgiref
lokal ausführbar
• Servername: localhost
• Port (beliebig): 8080
• app: WSGI-Anwendung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
41 / 295
Grundlagen Web
Proprietäre Integration
Benutzung wsgiref
Starten als lokales Python Programm
• python envwsgiref.wsgi
• Endung .wsgi nur für mod_python,
sonst auch .py möglich
• Log-Ausgabe je Anfrage auf Konsole
Benutzung mit IDEs
• Idle, Emacs oder Eclipse (PyDev)
• Debugging unterstützt, einfach
Breakpoint in Anwendung setzen und
mit Browser auf Anwendung zugreifen
• PyDev
• Endung muss .py sein
• Separate .wsgi-Datei, importiert nur
application-Objekt
2
3
• Beispiel env2.py und env2.wsgi
• Testen mit env2.py
Prof. Dr. Peter Barth (HS-RheinMain)
env2.wsgi
1
4
5
#!/usr/bin/python
import sys, os
sys.path.append(os.path.dirname(__file__))
os.chdir(os.path.dirname(__file__))
from env2 import application
Web-basierte Anwendungen
6. Juli 2015
42 / 295
Grundlagen Web
Proprietäre Integration
pydev – Python mit Eclipse
PyDev
• pydev.org
• Eclipse-Plugin, über
Eclipse-Marketplace
verfügbar und von
jedem selbst zu
installieren
• Vor Start einmal
Python-Interpreter
setzen
Python-Entwicklung
• Completion
• Debugging
• Subversion
Im Praktikum Idle oder Eclipse mit PyDev verwenden
(oder natürlich Emacs).
• ...
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
43 / 295
Grundlagen Web
werkzeug
Abstraktion von der dokumentenbasierten Web-Schnittstelle
Web-Anwendung
Abstraktion bei Web-Frameworks
• Anfrage erzeugt Antwort,
• Request-Objekt, Response-Objekt;
Textdokumente
kapselt Anfrage, Antwort
• Kommunikation in Anwendung hinein
• Kommunikat. von Anwendung heraus
• Schlüssel/Wert-Paare
• (HTML-)Dokument
Viele Konventionen:
•
→
REQUEST_METHOD: GET
bei GET-Anfrage
•
← Content-Type: text/html
bei HTML-Dokument als Antwort
• ...
Nicht komfortabel
Prof. Dr. Peter Barth (HS-RheinMain)
• Zugriff über Request-Objekt auf
Eigenschaften der Anfrage, z.B.
(Query)-Parameter, . . .
• Schlüssel/Wert-Paare
• der Eingabestrom
• Verändern des Response-Objekt, um
dann Text-Antwort zu erzeugen, z.B.
Typ setzen
Web-Abstraktion/Frameworks in Python
• werkzeug, einfache WSGI-Abstraktion,
verwenden wir
• Alternative webob, nur Req.-/Resp.
• django, Full-Stack Framework (sehr
schön, aber zu Lehrzwecken zu groß
und zu stark abstrahiert)
• ...
Web-basierte Anwendungen
6. Juli 2015
44 / 295
Grundlagen Web
werkzeug
Abstraktion mit werkzeug
werkzeug
werkzeug.pocoo.org
• Weit verbreitete WSGI-Abstraktion,
noch kein Web-Framework
1
from werkzeug.wrappers import Request, Response
2
• Python-Bibliothek
3
4
Features
5
• Request/Response Objekte
6
• URL-Routing, welche Funktion für
8
7
def app(environ, start_response):
request = Request(environ)
name = request.args.get("name", "Welt")
response = Response("Hallo %s" % name)
response.content_type="text/html"
return response(environ, start_response)
9
welche URL
if __name__ == ’__main__’:
from wsgiref.simple_server import make_server
12
httpd = make_server(’localhost’, 8080, app)
13
httpd.serve_forever()
10
11
• Cookies, Sessions, . . .
• Entwicklungsunterstützung:
automatisches Neuladen, Tracebacks
Einfaches Beispiel:
werkzeug.pocoo.org/docs/quickstart
(Tutorial passt nicht)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
45 / 295
Grundlagen Web
werkzeug
werkzeug Entwicklung
Request/Response häufig gebraucht
• @Request.application eine Abkürzung
1
2
3
4
• Nur noch ein Parameter, request
5
• Neue Instanz von Response zurück
7
6
werkzeug.serving, run_simple
8
9
10
from werkzeug.wrappers import Request, Response
@Request.application
def app(request):
name = request.args.get("name", "Welt")
fehler
return Response("Hallo %s" % name)
if __name__ == ’__main__’:
from werkzeug.serving import run_simple
run_simple(’localhost’, 8080, app,
use_reloader=True, use_debugger=True)
• Reloader: Bei Änderung der
Quelldatei wird automatisch die
Quell-Datei neu geladen; kein
Neustart mehr notwendig
• Debugger
• Aussagekräftige Fehlermeldung
• Cool: Interaktive Konsole im Browser
• Nachteil: Debugger (Eclipse)
funktioniert nicht mehr
• Beides verwenden je nach Bedarf
• Eclipse: use_reloader=False
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
46 / 295
Grundlagen Web
werkzeug
Standard WSGI-Rahmen
Für Deployment in Apache; für Entwicklung mit Debugging in Eclipse oder Entwicklung
mit Traceback und Debug-Konsole im Browser
werkzeugstd.wsgi
werkzeugstd.wsgi
• In passenden von Apache konfig.
Verzeichnis, public_html/wsgi
• Pfad zum Suchen der Quellen und
Wechslen in das Verzeichnis
1
2
3
4
5
6
• Laden eines Moduls und Funktion
application zur Verfügung stellen
werkzeugstd.py
#!/usr/bin/python
import sys, os
PATH="/home/mi/ssinn001/dev/stdapp"
sys.path.append(os.path.dirname(PATH))
os.chdir(os.path.dirname(PATH))
from werkzeugstd import application
werkzeugstd.py
#!/usr/bin/python
2 from werkzeug.wrappers import Request, Response
1
• Ein Python Modul im Suchpfad. Z. B.
unter ~ssinn001/dev/stdapp;
3
@Request.application
def application(rquest):
6
"the main application"
4
5
außerhalb von Apache konf.
Verzeichnis.
7
if __name__ == ’__main__’:
from werkzeug.serving import run_simple
10
run_simple(’localhost’, 8080, application,
11
use_reloader=True, use_debugger=True)
8
• Lokale Entwicklung; start; eingebauter
Web-Server
Prof. Dr. Peter Barth (HS-RheinMain)
9
Web-basierte Anwendungen
6. Juli 2015
47 / 295
Grundlagen Web
werkzeug
Request-Objekt
Request-Objekt
• request = Request(environ) (oder Parameter)
• Erzeugt aus Umgebungsvariablen
• Nur lesbar
Attribute, Methoden:
• Dokumentation:
werkzeug.pocoo.org/docs/wrappers
• method: GET, POST, . . .
• args: Dictionary aus Query-String (GET)
• form: Dictionary aus Formulareingaben (POST)
• values: kombiniert args und form
• url(base_url): URL (ohne Query-String)
• path: Angefragter Pfad
• environ: Die Original-Umgebungsvar.
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
48 / 295
Grundlagen Web
werkzeug
Response-Objekt
Response-Objekt
• response = Response(); ... ;
return response(environ, start_response)
(oder return response)
• Optional Inhalts-String
1
• Veränderbar, auch Liste als Inhalt
3
from werkzeug.wrappers import Request, Response
2
4
Attribute, Methoden:
5
6
7
• Dokumentation:
8
werkzeug.pocoo.org/docs/wrappers 9
• Iterable als Content (im Beispiel Liste) 10
11
• status, status_code: HTTP-Status
12
13
• headers: HTTP-Header
14
15
• automatically_set_content_length:
16
@Request.application
def app(request):
c = []
response = Response(c)
c.append("content modifiable\n")
response.status = "200 OK"
response.status_code = 200
c.append("response.headers:\n")
hs = response.headers
for k in hs.keys():
s=" %s: %s\n" % (k, hs[k])
c.append(s)
response.automatically_set_content_length = True
return response
Größe (HTTP/1.1 offene Verbindung!)
• charset: ’utf-8’
Prof. Dr. Peter Barth (HS-RheinMain)
Mehr mit Mixins; Dokumentation
Web-basierte Anwendungen
6. Juli 2015
49 / 295
Grundlagen Web
Routing
Routing – Welche Seite soll angezeigt werden?
Web-Anwendung
Beispiel: Apache Web-Server Hochschule
• von /˜<user>/... auf
• Mehrere „Seiten“ (Formulare)
• Nicht nur angepasst, sondern andere
Aufgabe (Produkt vs. Warenkorb)
• Je nach Anfrage (Routing) und
Zustand der Anwendung wird eine
andere Seite ausgewählt
Routing
• Statische Seiten/Inhalte: Abbildung
von Pfad in URL und Dateiname in
Ordnerhierarchie (z.B. index.html,
/static/style.css)
˜user/public_html/... (unter anderem
statische Inhalte wie Bilder, HTML-Seiten,
Style-Sheets, JavaScript-Dateien, . . . )
• von /˜<user>/cgi-bin/<app>.cgi/* auf
CGI-Skript
˜user/public_html/cgi-bin/<app>.cgi
• von /˜<user>/wsgi/<app>.wsgi/* auf
Python-Skript
˜user/public_html/wsgi/<app>.wsgi
• von allem mit Endung *.php auf
eingebetteten PHP-Interpreter
• Dynamisch: Abbildung von Pfad in
URL auf inhaltsgenerierende
Funktionalität (Skript)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
50 / 295
Grundlagen Web
Routing
Routing Beispiel – Statische Inhalte mit WSGI
from werkzeug.wrappers import Request, Response
import datetime, os
3 page = """<html><body><p>Es ist %(uhr)s Uhr</p>
4 <img src="statisch/uhr.png" /></body></html>"""
1
Statische Inhalte
2
• Statische Inhalte (Assets wie Bilder,
Style-Sheets) müssen in
Web-Anwendung zur Verfügung
gestellt werden
5
6
7
8
9
• In Produktiv-Umgebungen häufig
separater Server und separate URL,
bzw. Apache speziell konfiguriert
10
11
12
13
14
• Während Entwicklung mühselig
15
16
• Ziel: statische Inhalte während
17
Entwicklung
18
19
@Request.application
def application(request):
if request.path.startswith("/statisch"):
return statisch(request)
n = datetime.datetime.now()
uhr="%02d:%02d:%02d"%(n.hour,n.minute,n.second)
c = page % {’uhr’: uhr}
return Response(c, content_type="text/html")
def statisch(request):
base = os.path.dirname(__file__)
# fuehrendes / bei path wegmachen
asset_fname = os.path.join(base, request.path[1:])
asset = file(asset_fname).read()
return Response(asset)
Beispiel: Einfaches statisches Routing
• Wenn URL mit /statisch/ beginnt,
dann einfach nur Inhalt wiedergeben
• Ansonsten mit application
weitermachen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
51 / 295
Grundlagen Web
Routing
Optionen Code-Organisation
routing.py
Eine Python-Funktion je Seite
from werkzeug.wrappers import Request, Response
2 product_page = """%(name)s sind toll"""
3 def product(request, name):
4
return product_page % {’name’: name}
5 service_page = """Wir kuemmern uns um %(name)s"""
6 def service(request, name):
7
return service_page % {’name’: name}
8 @Request.application
9 def application(request):
10
name = "ACME Luftgitarren"
11
if request.path.startswith("/product"):
12
c = product(request, name)
13
elif request.path.startswith("/service"):
14
c = service(request, name)
15
else:
16
c = "nicht verfuegbar"
17
return Response(c)
1
• Klassisches Dispatching
• Mappen Anfrage auf Funktion
• Meist auf Basis der URL, geht auch
auf Grund des Zustands
Eine Python-Funktion für alles
• Ein Python-Funktion generiert alles
• Je nach Bedarf spezifisch Content
(durch Funktionsaufrufe) generieren
Kombiniert
• application-Objekt von WSGI als „ein
Skript für alles“, einfache API
• Darin Dispatcher, um unterschiedliche
Seiten zu generieren
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
52 / 295
Grundlagen Web
Routing
Einfache Routing-Abstraktion
1
Abbildung URL-Anfang auf Funktion
2
• Falls URL mit Schlüssel beginnt, dann
auf passende Funktion weiterleiten
3
4
5
6
• Vorbelegen von Funktionsparametern
möglich, nur ein Parameter (request)
erwartet
from werkzeug.wrappers import Request, Response
from routing import product, service
7
8
9
10
name = "ACME Luftgitarren"
url_map = {
’/product/’: lambda req: product(req, name),
’/service/’: lambda req: service(req, name),
# None default
None: lambda req: product(req, name)
}
11
Mächtigere Alternativen in Frameworks
• URL-Matcher – wann passt eine URL
auf ein Pattern
12
13
14
15
16
• Übernahme von Parametern aus URL
und Typkonvertierung automatisch
• Ausgefeilte Regelwerke und
17
18
@Request.application
def application(request):
for key in url_map:
if key and request.path.startswith(key):
return Response(url_map[key](request))
return Response(url_map[None](request))
Hinweis: Abbildung sollte eindeutig sein, sonst
ist Reihenfolge nicht definiert
Konfigurationsmöglichkeiten
• Beispiel: werkzeug
werkzeug.pocoo.org/docs/routing
(brauchen wir nicht)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
53 / 295
Grundlagen Web
Routing
Struktur Programmlogik
Hinweise
• Trennen HTML und Code, durch
Einsatz von Templating erledigt
• Einsatz von Funktionen und Objekten
für die Programmlogik
(Listen, Tabellen) da mächtigere
Templatesprache/Controls fehlen
4
5
6
7
8
10
11
Reihenfolge
page = """<html><head><title>Zaehler</title><head>
<body><form method="POST"> %(zahl)d <br/>
<input type="submit" name="up" value="Hoch" />
<input type="submit" name="down" value="Runter" />
<input type="hidden" name="zahl" value="%(zahl)d" />
</form></body>"""
@Request.application
def application(request):
12
• Programmzustand wiederherstellen
(Funktionalität Z15–Z18)
3
9
• Strikte Reihenfolge einhalten
• Berechnung neuer Zustand
from werkzeug.wrappers import Request, Response
2
• Ausgabe (Generieren HTML) meiden
• Ausnahme für dynamische Strukturen
(Auswertung Eingabe, Z13)
1
13
zahl = int(request.form.get("zahl", "0"))
14
15
16
17
18
if request.form.get("up", None):
zahl += 1
elif request.form.get("down", None):
zahl -= 1
19
• Ausgabe (Templating und
Ausgaberoutinen Z20–Z21)
Prof. Dr. Peter Barth (HS-RheinMain)
20
21
c = page % {’zahl’ : zahl}
return Response(c, content_type="text/html")
Web-basierte Anwendungen
6. Juli 2015
54 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Lebensdauer Daten über Anfragen hinaus
Lebensdauer Daten
Bisher Daten nur aus der Anfrage
• Block
• Kein Zustand in der Anwendung
• Daten nur in der Anfrage vorhanden
• Eigentlich kein Anwendungskontext
• Erste Ausnahme: Zähler zahl
• Programmiersprachenebene, {}
• Lokale Variablen
• Nicht spezifisch für Web
• Während einer Anfrage
Anwendungskontext
• Menge von Variable/Wert-Paaren
• Für Dauer Bearbeitung einer Anfrage
• In Prog.sprache Attribute des
request- Objekts,
Gültigkeitsbereiche
nicht globale Variablen
• Anwendung: Für alle gleich, für die
Lebensdauer der Anwendung,
Beispiel: Klick-Zähler
• Interaktionsfolge eines Anwenders
• In Prog.sprache nicht abgebildet
• Meist Speicher oder Dateisystem
• Session: Für jeden Anwender
unterschiedlich, für eine Folge von
Interaktionen eines Anwenders
(Session) gleich, Beispiel: Warenkorb
Prof. Dr. Peter Barth (HS-RheinMain)
• Session
• Anwendung
• Über Session hinaus
• In Prog.sprache nicht abgebildet
• Meist persistent (Dateisystem, DBMS)
Web-basierte Anwendungen
6. Juli 2015
55 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Sessions
Session
• im Mehrbenutzerbetrieb
• mehrere Interaktionen
• je Benutzer
• im gleichen Anwendungskontext
(bestehend aus Daten)
Welche Daten, Beispiele
• Benutzerdaten, Einstellungen,
Optionen, Einkaufskorb . . .
Problem
• HTTP-Protokoll ist zustandslos,
Anwendungskontext
Request/Response
• Keine Daten der letzten Anfrage-
bearbeitung in aktueller Anfrage
• Kontext-Daten manuell verwalten
Lösung – Explizite Session-Verwaltung
Prof. Dr. Peter Barth (HS-RheinMain)
• Ablage: Client (Browser) oder Server
Web-basierte Anwendungen
6. Juli 2015
56 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Anwendungskontext im Client
Anwendungskontext im Web-Browser
• Manuelle Weitergabe des Kontextes
von Seite zu Seite
• Techniken: Formulare (GET/POST),
Cookies
Vorteile
• Keine Belastung des Servers
(Datenhaltung)
• Keine eindeutige Zuordnung zu
Server-Session notwendig
Nachteile
• Datenübertragung je Anfrage
• Anfällig gegenüber Manipulationen
• Abhängig von Web-Client
• Garantie Persistenz schwierig
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
57 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Formulare
Formulare als Träger des
Anwendungskontextes
from werkzeug.wrappers import Request, Response
import cgi
3 _hidden = ’<input type="hidden"’
4 _hidden += ’ name="%(key)s" value="%(value)s" />’
1
2
• Verstecke Formularfelder
<input type="hidden"/>
5
• Alle Schlüssel/Wert-Paare
8
Weitergabe Anfrage
• POST: Datenstrom in Anfrage
6
7
9
10
11
12
def render_context(context):
entries = []
for k,v in context.iteritems():
v = cgi.escape(unicode(v), True)
entry = _hidden % {’key’: k, ’value’: v}
entries.append(entry)
return "\n".join(entries)
13
• GET: Query-String in Anfrage
(sichtbar)
Auswertung/Wiederherstellung
• Automatisch: request.form/args
14
15
16
17
18
19
20
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
@Request.application
def application(request):
ctx, args = {}, request.args
ctx[’a’] = args[’a’] if ’a’ in args else ’A’
ctx[’b’] = args[’b’] if ’b’ in args else ’B’
ctx[’z’] = int(args[’z’]) if ’z’ in args else 0
ctx[’z’] += 17
f =’<form>%s<input ’
f+=’type="submit" name="ok" value="Ok"/></form>’
c = f % render_context(ctx)
for k, v in ctx.iteritems():
c += "%s: %s<br />\n" % (k, v)
return Response(c, content_type="text/html")
Web-basierte Anwendungen
6. Juli 2015
58 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Generiertes Formular
1
Generiertes Formular
2
3
• Verstecke Eingabefelder für
4
Schlüssel/Wert-Paare
5
6
• name als Schlüssel
7
8
• value als Wert
9
<form>
<input type="hidden"
<input type="hidden"
<input type="hidden"
<input type="submit"
</form>
a: A<br />
b: B<br />
z: 17<br />
name="a" value="A" />
name="b" value="B" />
name="z" value="17" />
name="ok" value="Ok"/>
• Werden automatisch durch Formular
submit übertragen
• Formular selbst hat keine sichtbaren
Elemente außer dem Knopf
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
59 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Cookies
Ziel – Lokale Datenablage
• Daten im Browser speichern
• Über Neustart des Browsers
• Verwaltet durch Browser
• Server setzt, Browser sendet
automatisch zurück
• Missbrauch, Ad-Tracking
Cookies
• Namen: beliebig, max. 300 Cookies
• Wert: max. 4KB/Cookie (inkl. Namen)
• Verfallsdatum (Expires): Nach
Verfallsdatum ungültig, wird gelöscht
• Domain (Pfad): Nur an diese Domain,
max. 20 Cookies je Server/Domain
• secure: nur wenn gesichert übertragen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
60 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Cookies – Übertragung
Übertragung – HTTP, RFC 2109/6265
• Teil des HTTP-Headers
• Set-Cookie: <Name>=<Wert>,
Cookie: <Name>=<Wert>
• PATH=<path>
• SECURE (optional)
• URL-encodiert (cgi.escape), %FF
Ablauf – Beispiel
• Server schickt Set-Cookie-Header als
Teil der Antwort auf eine Anfrage
• Ab da schickt der Browser immer
wieder das Cookie in der Anfrage mit
Cookie-Header
• Mehrere Cookies in dem einen
Header-Feld
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
61 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Cookies setzen und lesen
setcookie.cgi
Manuell – CGI
1
• Header Set-Cookie
2
• Auslesen aus Umgebungsvariable
HTTP_COOKIE
4
3
• Inhalt sind Schlüssel=Wert Paare
5
6
7
8
#!/usr/bin/python
from datetime import datetime
print "Set-Cookie: jetzt="+ str(datetime.now())
print "Content-Type: text/html\n"
import cgi, cgitb
cgitb.enable()
import os
print "Cookie is: "+str(os.environ["HTTP_COOKIE"])
Mit Standardbibliothek
• Cookies auf dem Server mit Cookie
• Cookie.SimpleCookie als einfache
Abstraktion
• Schreiben und Lesen
1
2
• Mehrere Schlüsselwert-Paare im
3
4
Beispiel
5
Set-Cookie: jetzt="2013-03-11
19:52:47.505760"; Path=/
• Löschen: Leerer Inhalt und
überschrittenes Verfallsdatum
Prof. Dr. Peter Barth (HS-RheinMain)
6
7
8
9
10
#!/usr/bin/python
from datetime import datetime
import os, cgi, Cookie
cookie = Cookie.SimpleCookie()
cookie["jetzt"] = str(datetime.now())
cookie["jetzt"]["path"] = "/"
print cookie.output()
print "Content-Type: text/html\n"
rc = Cookie.SimpleCookie(os.environ["HTTP_COOKIE"])
print "Cookie is: "+rc["jetzt"].value
print cookie.output()
11
Web-basierte Anwendungen
6. Juli 2015
62 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Cookies
1
Cookie als Träger des
Anwendungskontextes
2
3
4
• Schlüssel/Wert-Paare in Cookies
5
6
• Alternativ zusammenpacken als
7
Query-String in einem Cookie möglich
Weitergabe zum Browser
• set_cookie
8
9
10
12
14
15
Auswertung/Wiederherstellung
16
17
• Mit request.cookies
18
19
20
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
def render_context(content, context):
response = Response(content,
content_type="text/html")
for k,v in context.iteritems():
v = cgi.escape(unicode(v), True)
response.set_cookie(k, v)
return response
11
13
• Automatisch, über Header
from werkzeug.wrappers import Request, Response
import cgi, Cookie
@Request.application
def application(request):
ctx, args = {}, request.cookies
ctx[’a’] = args[’a’] if ’a’ in args else ’A’
ctx[’b’] = args[’b’] if ’b’ in args else ’B’
ctx[’z’] = int(args[’z’]) if ’z’ in args else 0
ctx[’z’] += 17
c = []
response = render_context(c, ctx)
c.append(’<form><input type="submit" ’)
c.append(’
name="ok" value="Ok"/>’)
c.append(’</form>’)
for k,v in request.cookies.iteritems():
c.append("%s: %s<br />\n" % (k, v))
return response
Web-basierte Anwendungen
6. Juli 2015
63 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Sichere Cookies
Verschlüsseln der Daten auf dem Client
• Schwierig zu lesen, zu manipulieren
• Einfache Sicherung von Manipulation:
Fingerabdruck
Verhindern von Manipulationen
• Wenn Daten verändert wurden, dann
stimmt der „Fingerabdruck“ der Daten
nicht mehr
• Standardalgorithmus SHA1
• Eingabe ist beliebiger String (Zahl)
• Ergebnis ist ein 160-Bit
Message-Digest (meist als Hex-String
dargestellt)
• Alternativen: MD5, SHA256, SHA512
Geheimes Salz
• Nachricht muss (teilweise) geheim
sein, sonst kann Digest berechnet
werden
• Lösung: Hinzufügen eines geheimen
„Salzes“ (Salt), ein beliebiger String
Python und SHA1 (und andere)
• hashlib.sha1(message).hexdigest()
Prof. Dr. Peter Barth (HS-RheinMain)
RFC 3174 „ The SHA-1 is called secure
because it is computationally infeasible to
find a message which corresponds to a
given message digest, or to find two
different messages which produce the
same message digest. Any change to a
message in transit will, with very high
probability, result in a different message
digest, and the signature will fail to verify.“
a99219f952208c68c30cb4a99cf9d7902e395702
Web-basierte Anwendungen
6. Juli 2015
64 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Sichere Cookies, Beispiel
ctxlib.py
1
import urllib, hashlib, urlparse
2
3
4
5
6
7
8
9
2
SALT="secret"
CONTEXT, FP = "context", "fingerprint"
def render_context(response, context):
ctx = urllib.urlencode(context)
response.set_cookie(CONTEXT, ctx)
fp = hashlib.sha1(ctx+SALT).hexdigest()
response.set_cookie(FP, fp)
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
3
4
5
Prof. Dr. Peter Barth (HS-RheinMain)
form = """<form><input type="submit" name="ok"
value="Ok"/></form>"""
6
7
8
9
10
def restore_context(request):
if CONTEXT not in request.cookies:
return {}
if FP not in request.cookies:
raise Exception("Security Error")
ctx = request.cookies[CONTEXT]
fp = hashlib.sha1(ctx+SALT).hexdigest()
if fp != request.cookies[FP]:
raise Exception("Security Error")
ret = urlparse.parse_qs(ctx)
for k in ret: # nur ein Wert
ret[k] = "".join(ret[k])
return ret
from werkzeug.wrappers import Request, Response
import cgi, Cookie, ctxlib
11
12
13
14
15
16
17
18
19
20
@Request.application
def application(request):
ctx = ctxlib.restore_context(request)
if ’a’ not in ctx: # uninitialisiert
ctx[’a’], ctx[’b’] = ’A’, ’B’
ctx[’z’] = ’0’
c = [form]
for k,v in ctx.iteritems():
c.append("%s: %s<br />\n" % (k, "".join(v)))
ctx[’z’] = int(ctx[’z’]) # Typkonversion
ctx[’z’] += 17 # Zustandsfortschritt
response = Response(c,content_type="text/html")
ctxlib.render_context(response, ctx)
return response
Web-basierte Anwendungen
6. Juli 2015
65 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Kontext im Client – Sichere Cookies, werkzeug
SecureCookie
1
in werkzeug
2
3
• Teil der Erweiterungen
4
• Verfolgt vorgestelltes Prinzip
5
• Leeres Dictionary bei Manipulation
7
4
5
6
7
9
11
import werkzeug.contrib.securecookie as sc
12
SALT="secret"
CONTEXT="context"
def render_context(response, ctx):
scs = sc.SecureCookie(ctx, SALT).serialize()
response.set_cookie(CONTEXT, scs)
10
11
12
13
14
15
16
13
14
15
16
17
18
8
9
8
10
2
3
form = """<form><input type="submit" name="ok"
value="Ok"/></form>"""
6
ctxlibwerkzeug.py
1
from werkzeug.wrappers import Request, Response
import cgi, Cookie, ctxlibwerkzeug as ctxlib
def restore_context(request):
if CONTEXT not in request.cookies:
return {}
scs = request.cookies[CONTEXT]
ctx = sc.SecureCookie.unserialize(scs, SALT)
if not ctx:
raise Exception("Security Error")
return ctx
19
20
@Request.application
def application(request):
ctx = ctxlib.restore_context(request)
if ’a’ not in ctx: # uninitialisiert
ctx[’a’], ctx[’b’] = ’A’, ’B’
ctx[’z’] = 0
c = [form]
for k,v in ctx.iteritems(): # Typ bleibt int
c.append("%s:%s<br />\n"%(k,"".join(str(v))))
# keine Typkonversion mehr notwendig
ctx[’z’] += 17 # Zustandsfortschritt
response = Response(c,content_type="text/html")
ctxlib.render_context(response, ctx)
return response
• Serialisierung nicht als Querystring,
default pickle
• Typinformation bleibt erhalten
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
66 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Anwendungskontext im Server
Verwalten des Anwendungskontextes in
der Web-Anwendung im Server
Session-ID
Anwendungskontext
• Ein Datensaz je Session
• Session-Identifikation über Browser
notwendig
• HTTP ist zustandslos
Vorteile
• Unabhängig(er) von Client
• Schwieriger zu manipulieren
• Persistenz
Nachteile
• Hohe Belastung des Servers
• Session-Zuordnung trotzdem
notwendig im Browser
• Session-Id
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
67 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Identifikation im Web-Browser
Session-Identifikation (Session-ID)
• Ein eineindeutiger Bezeichner je
Session
• Nicht vorhersagbar
• IP-Adresse, Benutzerkennung und Zeit
weder ausreichend noch hinreichend
• Internet-Café, anonymer
Mehrbenutzerbetrieb
• Dynamisches Ändern der IP-Adresse
• Meist Hash einer Zufallszahl plus
IP-Adresse (99,9...%-Lösung)
Session-ID in Python erzeugen
• import uuid, OpenSSL
Verwalten der Session-ID
r = OpenSSL.rand.bytes(16)
• Bevorzugt im Cookie
sessionid = uuid.UUID(bytes=r)
• Oft optional im GET-String
#38e9aa97-fe49-2ea2-4d92-4aa0369942a0
• Falls Cookie verboten
• Navigation mit location: geht schief
(und ist verboten!)
Prof. Dr. Peter Barth (HS-RheinMain)
• „Gute“ Zufallszahl
• Ausreichend eindeutig
Web-basierte Anwendungen
6. Juli 2015
68 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Verwaltung auf Server
Zusammenspiel Session-ID und Kontext
• Inhalte des Anwendungskontextes
werden auf Server gehalten
• Assoziation von Session-ID mit
Anwendungskontext
Datenhaltung auf Server, Alternativen
• Hauptspeicher/Prozesskontext: Bei
CGI/WSGI nicht (einfach) möglich,
Zuordnung Anfrage auf Prozess bei
CGI immer neu oder durch
Web-Server gesteuert
Hauptspeicher/
Prozesskontext
Server/Host
• Datei: Aufwändig, viele kleine Dateien
schreiben, Session-ID ist Dateiname
• Datenbank : Sicher, aufwändig,
Web-Server/Skriptumgebung
Dateisystem
Datenbank
unabhängig von Web-Server
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
69 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Verwaltung auf Server mit Python
Anwendungskontext Verwendung
• Session-Objekte, Middleware
• Middleware entscheidet über Ort und
Art der Speicherung
Session-ID
• Bevorzugt mit sicheren Cookies
Web-Server/Skriptumgebung
Ablage zwischen den Seitenaufrufen
• Prozesskontext des Skripts hat keine
Bedeutung! Je Anfrage andere Python
Instanz möglich oder (CGI) immer neu
Shared
Memory
Dateisystem
Server/Host
• Serialisierung meist mit pickle,
beliebige Python-Objekte möglich
• Ablageort meist Dateisystem und
Datenbank; Client (Cookie) oder
Hauptspeicher (Shared Memory,
memcached) möglich
Prof. Dr. Peter Barth (HS-RheinMain)
Datenbank
Web-basierte Anwendungen
6. Juli 2015
70 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Verwaltung auf Server – Session-ID
session_id
Session-ID
import os, uuid, OpenSSL
2 import werkzeug.contrib.securecookie as sc
1
• Wenn schon vorhanden, dann einfach
als String zurückgeben
3
4
SESCOOKIE, SALT = "session_cookie", "secure"
5
• Wenn nicht, dann vorher erzeugen
6
7
• Session-ID in einem Cookie
• Header setzen beim Erzeugen
8
9
10
• Antwort muss also schon zur
11
Verfügung stehen
12
• Holen und gegebenenfalls erzeugen
13
14
15
16
17
18
def get_sid(request, response):
"session_id aktueller session, ggf. neu"
if SESCOOKIE not in request.cookies: # new
r = OpenSSL.rand.bytes(16)
sid = str(uuid.UUID(bytes=r))
cd = {SESCOOKIE: sid}
c = sc.SecureCookie(cd, SALT).serialize()
response.set_cookie(SESCOOKIE, c)
else:
c = request.cookies[SESCOOKIE]
cd = sc.SecureCookie.unserialize(c, SALT)
sid = cd[SESCOOKIE]
return sid
Verwendung
• Vor jeder Anfrage: Inhalte auslesen
• Nach jeder Anfrage: Inhalte speichern
• Ablageort beliebig: Schlüssel (zum
Beispiel Dateiname) ist die Session-ID
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
71 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Verwaltung auf Server – Dateisystem
session_fs
Sessions selbst gemacht (Verständnis)
• Session-Objekt einfaches Dictionary
mit Eintrag ’_SID’ auf Session-ID
1
2
3
4
5
• Ablage in Dateisystem
6
7
• Keine Überprüfung ob Kollision
8
(schon vorhandene Session)
• Kein Aufräumen alter Sessions
import os, cPickle
9
STORAGE, SID = "/tmp", "_SID"
def load_session(session_id):
fpath = os.path.join(STORAGE, session_id)
try:
return cPickle.load(file(fpath, "r"))
except (IOError, EOFError) as e:
return {SID: session_id}
10
def save_session(session):
fpath = os.path.join(STORAGE, session[SID])
13
cPickle.dump(session, file(fpath, "w"))
11
Verwendung, als Middleware
12
• response erzeugen, Session-Objekt
14
15
mit load_session erhalten
16
17
• Die Anwendung manipuliert das
Session-Objekt (nicht _*)
18
19
• save_session am Ende zum Speichern
20
def kill_session(session):
fpath = os.path.join(STORAGE, session[SID])
try:
os.unlink(fpath)
except OSError:
pass
aktualisierter Session Inhalte
• kill_session Session technisch
beenden (vermeiden)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
72 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Session-Verwaltung auf Server – Beispiel
from werkzeug.wrappers import Request, Response
from session_id import get_sid
3 from session_fs import load_session, save_session
1
Anforderung Anwendung
2
• Hochzählen Zähler je Anfrage, Start
17, je Benutzer unabhängig
4
5
page = file("page.tpl").read()
6
Umsetzung, wie beschrieben
7
8
• Anwendung selbst in doit ausgelagert
• Sessionverwaltung ist Middleware
9
10
11
12
13
14
15
def doit(request, session):
if "zahl" not in session:
session["zahl"] = 16
session["zahl"] += 1
lis = []
for k,v in session.iteritems():
if not k.startswith("_"):
lis.append("%s = %s" % (str(k), str(v)))
return "<br />\n".join(lis)
16
17
18
19
20
21
22
23
24
25
26
27
Prof. Dr. Peter Barth (HS-RheinMain)
@Request.application
def application(request):
c = []
response = Response(c, content_type="text/html")
sid = get_sid(request, response)
session = load_session(sid)
content = doit(request, session)
save_session(session)
dic = {’title’:’Sessions’, ’content’: content}
c.append(page % dic)
return response
Web-basierte Anwendungen
6. Juli 2015
73 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Achtung beim Schreiben – Nebenläufigkeit!
Parallele Zugriffe
• Dateien liegen auf einem Server
• Mehrere Zugriffe auf die Datei zur
selben Zeit ist möglich
• Selbst bei einer Session-Datei, zum
Beispiel bei <iframe> (böse)
• Lesender/schreibender Zugriff
WebServer
Konflikte – Unklares Verhalten
• Gleichzeitiges Schreiben und Lesen
Dateisystem
• Was wird gelesen? Halb/halb?
• Was wird geschrieben? Noch
konsistent?
Server/Host
Lösung – Sperren
• Verwaltung exklusiver Zugriffsrechte
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
74 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Sperren mit Dateien
session_fslock
Exklusiver Zugriff auf Datei, fnctl
import os, cPickle, fcntl
2 from session_fs import STORAGE, SID, kill_session
1
• flock(fd, operation) für
3
Sperrverwaltung, nur Unix
4
5
• fd File descriptor, offene Datei
• Konstanten LOCK_* in fnctl
6
7
8
Operationen (Auszug)
9
10
• LOCK_EX (2): Exklusiver Zugriff, warten 11
wenn (eine) Sperre noch vergeben
12
13
• LOCK_UN (8): Sperre freigeben
14
15
Verwendung
16
17
• Klammer um Anfang/Ende der
18
Anwendung (kompletter Request ist
kritischer Bereich aus Session-Sicht)
• lockf, thread-sicher Sperre erhalten
19
20
21
22
23
24
• _*-Schlüssel, interne Merker
25
• ToDo: Schließen im Fehlerfall, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
FKEY, LOCKFKEY = "_file", "_lockfile"
def load_session(session_id):
fpath = os.path.join(STORAGE, session_id)
lockf = file(fpath, "a") # force open file
fcntl.flock(lockf, fcntl.LOCK_EX)
f = file(fpath, "r+") # lock to read and write
try:
session = cPickle.load(f)
except EOFError:
session = {}
session[SID] = session_id
session[LOCKFKEY] = lockf
session[FKEY] = f
return session
26
def save_session(session):
f = session[FKEY]; del session[FKEY]
lockf = session[LOCKFKEY]; del session[LOCKFKEY]
f.seek(0) # Anfang von Datei
cPickle.dump(session, f)
f.close()
fcntl.flock(lockf, fcntl.LOCK_UN)
lockf.close()
Web-basierte Anwendungen
6. Juli 2015
75 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Sessions mit werkzeug
1
Realisieren von Sessions
2
• Komplexe, fehleranfällige Middleware
3
4
• Vorgefertigte Lösungen verwenden
5
• Beispiel: werkzeug.contrib.sessions
7
6
8
9
werkzeug.contrib.sessions
10
• Vorgefertigte Bibliothek, kümmert sich 11
12
um Cookies und Storage
from werkzeug.wrappers import Request, Response
import werkzeug.contrib.sessions as sessions
13
page = file("page.tpl").read()
def doit(request, session):
if "zahl" not in session:
session["zahl"] = 16
session["zahl"] += 1
lis = []
for k,v in session.iteritems():
lis.append("%s = %s" % (str(k), str(v)))
return "<br />\n".join(lis)
14
• Eine Option ist Dateisystem zum
15
speichern (Z23)
16
17
• Als Middleware einfach zwischen
18
19
Aufruf und Ergebnis setzen
20
ToDo
@Request.application
def app(request):
session = request.environ["werkzeug.session"]
content = doit(request, session)
dic = {’title’:’Sessions’, ’content’: content}
return Response(page%dic,content_type="text/html")
21
# haenge die Middleware dazwischen
fsstore=sessions.FilesystemSessionStore()
24 application=sessions.SessionMiddleware(app,fsstore)
22
• Aufräumen abgelaufener Sessions
23
• Weitere Konfiguration möglich
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
76 / 295
Grundlagen Web
Zustandsverwaltung über Anfrage hinaus
Sessions
Sessions – Umsetzung
• Serialisieren häufig notwendig
• Versuchen wenig Daten in der Session zu halten
• Keine Frames, kein <iframe>, konkurrierende Zugriffe
Dateien als Ablage und sperren häufig nicht beste Wahl
• Funktioniert nur gut auf einem Dateisystem auf einem Rechner (/tmp), nicht gut
bei Network File System (NFS)
• Skaliert nicht immer wie gewünscht, ursprünglich nicht gebaut für sehr viele
Anfragen/Dateien gleichzeitig
• Einsatzgebiete: Sessions, Zugriffszähler, Kommentare in Blogs, . . .
Alternativen für Speicher-Backend
• tmpfs, shared memory mit locking
• Datenbanken
• Sichere Ablage von Daten
• Design für gleichzeitigen schreibenden und lesenden Zugriff
• Ausgefeilte Sperrverwaltung, Transaktionskonzept (ACID)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
77 / 295
Grundlagen Web
Bibliotheken
Integration von Datenbanken
Datenbanken für Web-Anwendungen
• Mehrbenutzeranw., immer mit dabei
• Alle Anwendungsdaten, auch
Session-Daten möglich
Datenbanken und Python
• Anbindung aller wichtigen DBMS
• Python DB-API 2.0 (PEP 249) definiert
DB-unabhängige Schicht (Pythons
JDBC)
• Treiber für spezifische DBMS
Python DB-API 2.0
psycopg2
Datenbank
Postgresql und Python
• Treiber psycopg2, pygresql
• Postgresql wie gewohnt (auch auf
ZuHause-Image)
Frameworks: Tools, ORM, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
78 / 295
Grundlagen Web
Bibliotheken
Web-Seite mit Python und Postgresql – Beispiel
1
Verwendung, wie gewohnt
2
3
• Verbindungsaufbau connect
5
• Anfragen mit execute, Parameter
• www.python.org/dev/
peps/pep-0249/
page = file("page.tpl").read()
keys = ["vorname", "name", "matnr"]
6 tline = "<tr> %s </tr>"
7 thead, tdata = "<th>%s</th> ", "<td>%s</td> "
4
• cursor-Konzept, je Verbindung
verwenden!
8
9
10
11
12
13
• initd.org/psycopg/
from werkzeug.wrappers import Request, Response
import psycopg2 as pg
14
15
16
17
def doit(request):
conn = pg.connect(host="localhost", database="medieninf",
user="medieninf", password="medieninf")
curs = conn.cursor()
curs.execute("SELECT "+", ".join(keys)+" FROM studi")
lis = [tline%"".join(map(lambda k:thead%k,keys))]
for row in curs.fetchall():
lis.append(tline%"".join(map(lambda k:tdata%k,row)))
return "<table>\n" + "\n".join(lis) + "</table>\n"
18
@Request.application
def application(request):
21
content = doit(request)
22
dic = {’title’:’Namen’, ’content’: content}
23
return Response(page%dic,content_type="text/html")
19
20
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
79 / 295
Grundlagen Web
Bibliotheken
Sicheres Parametrisieren von Anfragen
Externe Daten sind bedrohlich
• Benutzereingabe, Datei, DB, . . .
• Verhindere SQL-Injection
Umsetzung mit execute
• Wie %-Operator, extra Argument
1
keys = ["vorname", "name", "matnr"]
tline = "<tr> %s </tr>"
5 thead, tdata = "<th>%s</th> ", "<td>%s</td> "
3
4
6
7
8
9
10
• Tupel oder Dictionary
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Prof. Dr. Peter Barth (HS-RheinMain)
import psycopg2 as pg
2
def doit(request):
conn = pg.connect(host="localhost", database="medieninf",
user="medieninf", password="medieninf")
curs = conn.cursor()
sql = "SELECT "+", ".join(keys)+" FROM studi "
if "matnr" in request.args:
sql += "WHERE matnr = %s"
curs.execute(sql, (request.args["matnr"], ))
elif "vorname" in request.args:
sql += "WHERE vorname LIKE %(vorname)s"
like = "%%" + request.args["vorname"] + "%%"
curs.execute(sql, {’vorname’: like})
else:
curs.execute(sql)
lis = [tline%"".join(map(lambda k:thead%k,keys))]
for row in curs.fetchall():
lis.append(tline%"".join(map(lambda k:tdata%k,row)))
return "<table>\n"+"\n".join(lis)+"</table>\n"
Web-basierte Anwendungen
6. Juli 2015
80 / 295
Grundlagen Web
Bibliotheken
Sicheres Parameterisieren von Anfragen – Ernst gemeint
initd.org/psycopg/docs/usage.html
Spalten-/Tabellennamen
• Schlüssel vorname, name, matnr sind keine Variablen, kommen im Quelltext vor
• Falls Spalten-/Tabellennamen dynamisch gesetzt werden, dann müssen diese per
String-Operation integriert werden
• Dann manuell prüfen zum Beispiel mit Dictionary, wobei Schlüssel die
Benutzereingabe ist und der Wert im Quelltext steht
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
81 / 295
Grundlagen Web
Bibliotheken
Postgresql und Python – Hinweise/1
Ergebnisse als Dictionary
• Spaltenname statt Position in
Anfrage für Wertzugriff
• Verständlicherer Quellcode
• psycopg2.extras.DictCursor
Unicode Daten
• Unicode-Daten kann
Encoding-Probleme hervorrufen
• Datenbank Unicode statt ASCII
liefern lassen
• extensions.register_type
• Aktueller cursor liefert statt
String immer unicode
• Wenn alles immer Unicode ist,
dann gibt es keine Probleme
Prof. Dr. Peter Barth (HS-RheinMain)
>>> import psycopg2.extras as extras
>>> curs=conn.cursor(cursor_factory=extras.DictCursor)
>>> curs.execute("SELECT * FROM studi")
>>> row = curs.fetchone()
>>> row
[815, ’Susi’, ’Sinnlos’]
>>> row["vorname"]
’Susi’
>>> import psycopg2.extensions as extensions
>>> curs = conn.cursor()
>>> sql = "INSERT INTO studi (matnr, vorname, name) "
>>> sql += " VALUES (%s, %s, %s)"
>>> curs.execute(sql, (1, u’Jörg’, u’Üßgärö’))
>>> conn.commit()
>>> curs.execute("SELECT * FROM studi WHERE matnr=1")
>>> curs.fetchone()
(1, ’J\xc3\xb6rg’, ’\xc3\x9c\xc3\x9fg\xc3\xa4r\xc3\xb6’)
>>> curs = conn.cursor()
>>> extensions.register_type(extensions.UNICODE, curs)
>>> curs.execute("SELECT * FROM studi WHERE matnr=1")
>>> curs.fetchone()
(1, u’J\xf6rg’, u’\xdc\xdfg\xe4r\xf6’)
>>> curs.execute("SELECT * FROM studi WHERE matnr=1")
>>> print(", ".join(curs.fetchone()[1:]))
Jörg, Üßgärö
Web-basierte Anwendungen
6. Juli 2015
82 / 295
Grundlagen Web
Bibliotheken
Postgresql und Python – Hinweise/2
Thread-Sicherheit
• Sowohl Datenbankverbindung
als auch Cursor ist thread-sicher
• Connection-Pools separat
• Praktikum: eine Verbindung conn
je Anfrage
Transaktionen
• Eine Transaktion je Verbindung
• Start automatisch, beenden mit
commit
>>> curs = conn.cursor()
>>> curs.execute("INSERT INTO studi (matnr,vorname,name)"+
" VALUES (777,’Votan’,’Wahnwitz’)")
>>> conn.rollback()
>>> sql = "SELECT vorname FROM studi WHERE matnr=777"
>>> curs.execute(sql)
>>> curs.fetchall()
[]
>>> curs.execute("INSERT INTO studi (matnr,vorname,name)"+
" VALUES (777,’Votan’,’Wahnwitz’)")
>>> conn.commit()
>>> curs.execute(sql)
>>> curs.fetchall()
[(’Votan’,)]
oder rollback
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
83 / 295
Grundlagen Web
Bibliotheken
Web-Anwendungen und Transaktionen
Transaktion – DB-Sicht
• Alles zwischen Begin (auto) und Ende
• DBMS kümmert sich um transaktionale Sicherung
Transaktion – Anwendersicht, Logical Unit of Work
• Formular-Feld gesehen, geändert, gespeichert
• Formular-Feld darf sich zwischenzeitlich nicht geändert haben
• Mindestens zwei! Anfragen: Lesen in erster (Formular aufbauen), Schreiben in
zweiter (Formular auswerten)
• Mit einer Transaktion alleine nicht durchführbar
Aufgabe: Transaktional sicher arbeiten
• Lesen in erster Anfrage, sichern Werte in Session
• Vor Schreiben Kontroll-Lesen in zweiter Anfrage, Abbruch bei Änderung
• Kontrolllesen innerhalb Transaktion
Hinweis
• FOR UPDATE nicht vergessen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
84 / 295
Grundlagen Web
Bibliotheken
Hochladen von Dateien
fileupload.tpl
Spezielles HTML-Formular
<form enctype="multipart/form-data" method="POST">
2 Datei: <input type="file" name="binarydata" /><br />
3 <input type="submit" value="Hochladen" />
4 </form>
1
• enctype="multipart/form-data",
POST, <input type="file»
Verarbeiten mit werkzeug, request.files
• Schlüssel name von file
1
2
• FileStorage-Attribute, lesen von stream 3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Prof. Dr. Peter Barth (HS-RheinMain)
from werkzeug.wrappers import Request, Response
page = file("page.tpl").read()
form = file("fileupload.tpl").read()
KEY="binarydata"
def doit(request):
if request.method == "GET": return form
if not request.files[KEY]:
return u"<p>Keine Datei</p>"+form
ls, fs = [], request.files[KEY]
ls.append("Content-Type: %s"%fs.content_type)
ls.append("Dateiname (Client): %s"%fs.filename)
ls.append("len: %d (meist 0)"%fs.content_length)
blob = fs.stream.read()
lines.append("Content-Length: %d " % len(blob))
return "<br />".join(ls)
@Request.application
def application(request):
content = doit(request)
dic = {’title’:’File Upload’, ’content’: content}
return Response(page%dic,content_type="text/html")
Web-basierte Anwendungen
6. Juli 2015
85 / 295
Grundlagen Web
Bibliotheken
Speichern Binärdatei in Datenbank
1
Passende Datenbank-Struktur
3
• Tabelle mit Referenzinformation
4
• id, zum Wiederfinden
• name, mime, size sind Extras
5
6
7
• Datei als BLOB (Binary Large Object)
mit lobject in Datenbank ablegen
8
9
10
11
CREATE TABLE bin (
id OID PRIMARY KEY,
name TEXT, mime TEXT, size INT
);
...
medieninf=> SELECT * FROM bin;
id |
name
| mime | size
-------+-----------------+-----------+------31693 | fileupload1.png | image/png | 22595
31694 | fileupload2.png | image/png | 29164
medieninf=> \lo_list
Large objects
ID | Owner | Description
-------+-----------+------------31693 | medieninf |
31694 | medieninf |
Prof. Dr. Peter Barth (HS-RheinMain)
import psycopg2 as pg
2
12
13
14
15
16
17
18
KEY="binarydata"
def doit(request): # wenn POST
fs = request.files[KEY]
blob = fs.stream.read()
conn = pg.connect(host="localhost", database="medi
user="medieninf", password="medieninf")
lo = conn.lobject(0, "wb")
lo.write(blob)
sql = "INSERT INTO bin VALUES "
sql += "(%(loid)s, %(name)s, %(type)s, %(size)s)"
data = {’loid’: lo.oid, ’name’: fs.filename,
’type’: fs.content_type, ’size’: len(blob)}
curs = conn.cursor()
curs.execute(sql, data)
conn.commit()
return "uploaded " + str(lo.oid)
Hochladen mit POST
• Anlegen large object, id
• Eintragen Meta-Information in Tabelle
• Muss in Transaktion sein
Web-basierte Anwendungen
6. Juli 2015
86 / 295
Grundlagen Web
Bibliotheken
Laden Binärdatei von Datenbank
1
Download Datei von DBMS
• Verwaltungsinformationen anhand von
id lesen, zum Beispiel aus
Query-Parameter id
7
8
9
Datei mit passenden mimetype zur
Ausgabe
3
4
5
6
7
8
9
10
11
12
13
4
6
• Rückgabe von Inhalt der kompletten
2
3
5
• Lesen des BLOB mit lobject
1
import psycopg2 as pg
2
10
11
12
13
14
def doit(id): # get image
conn = pg.connect(host="localhost", database="medi
user="medieninf", password="medieninf")
sql = "SELECT mime FROM bin WHERE id = %s"
curs = conn.cursor()
curs.execute(sql, (id, ))
res = curs.fetchone()
if not res:
return "Kein Image", "text/plain"
lo = conn.lobject(id, "rb")
blob = lo.read()
return blob, res[0]
from werkzeug.wrappers import Request, Response
from dbdownloaddoit import doit
page = file("page.tpl").read()
img = ’<img src="?id=%(id)d" />’
@Request.application
def application(request):
if "id" in request.args: # das Bid selbst
c,mime = doit(int(request.args["id"]))
return Response(c, content_type=mime)
else: # Rahmen fuer ein Beispielbild
c = img % {’id’: 31693}
dic={’title’:’Image Show’,’content’:c}
return Response(page%dic,content_type="text/html")
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
87 / 295
Grundlagen Web
Bibliotheken
Bilder erzeugen mit Image
Alle bekannten Bibliotheken verwendbar
1
2
3
• Beispiel Image
4
• Zeichnen mit ImageDraw,
5
• Fonts mit ImageFont
7
6
8
Anzeige eines selbstgebauten Buttons
9
10
• Laden eines Templates button.png
11
12
• Füllen des Templates mit Text, Text in
spezifischem Font (Vera) und Größe
(16)
13
14
15
16
17
• Ausgabe in String über cStringIO da
nur save möglich
Beispiel
18
19
20
21
from werkzeug.wrappers import Request, Response
import Image, ImageDraw, ImageFont, cStringIO
fp = "/usr/share/fonts/truetype/"
fp += "ttf-bitstream-vera/Vera.ttf"
fontsize = 16
@Request.application
def application(request):
text = "Button"
if "text" in request.args:
text = request.args["text"]
im = Image.open("button.png")
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(fp, fontsize)
twidth, theight = font.getsize(text)
imwidth, imheight = im.size
x, y = imwidth/2-twidth/2, imheight/2-(theight/2)
draw.text((x, y), text, font=font, fill="black")
output = cStringIO.StringIO()
im.save(output, format="PNG")
content = output.getvalue()
return Response(content,content_type="image/png")
• button.png
• http://localhost:8080
• http:.../?text=HS+RheinMain
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
88 / 295
Grundlagen Web
Bibliotheken
PDF erzeugen mit reportlab
from werkzeug.wrappers import Request, Response
import cStringIO
3 from reportlab.pdfgen import canvas
1
reportlab
2
• Bibliothek (frei) zum Erzeugen von
PDF
4
5
6
• Auch Formulare und Textsatz möglich
• Einfaches Beispiel mit absoluter
7
8
9
Positionierung (auf Canvas zeichnen), 10
11
Quadratzahlen
12
13
14
15
16
17
18
19
20
Prof. Dr. Peter Barth (HS-RheinMain)
@Request.application
def application(request):
output = cStringIO.StringIO()
c = canvas.Canvas(output)
i = 0
for x in range(50, 500, 85):
c.drawString(x, 810, "Zahl")
c.drawString(x+33, 810, "Quadrat")
for y in range(50, 800, 15):
c.drawString(x, 842-y, "%d"%i)
c.drawString(x+33, 842-y, "%d"%(i*i))
i += 1
c.save()
content= output.getvalue()
mime = "application/pdf"
return Response(content,content_type=mime)
Web-basierte Anwendungen
6. Juli 2015
89 / 295
Grundlagen Web
Bibliotheken
XML verarbeiten mit lxml
lxml
• etree, Elementree, einfaches DOM
• Einfacher Zugriff auf Unterelemente
mit find, auf Attribute mit get
• Viel mehr möglich
1
2
from werkzeug.wrappers import Request, Response
from lxml import etree
3
4
5
6
7
8
9
10
11
12
13
14
@Request.application
def application(request):
xmlstring = file("adressen.xml").read()
addrs = etree.fromstring(xmlstring)
l = []
for addr in addrs:
anrede = addr.find("anrede").get("bez")
name = addr.find("name").text
l.append("%s %s" % (anrede, name))
c = "<br />\n".join(l)
return Response(c,content_type="text/html")
Prof. Dr. Peter Barth (HS-RheinMain)
<?xml version="1.0" encoding="UTF-8"
standalone="no" ?>
<!DOCTYPE adressen SYSTEM "adressen.dtd">
<adressen>
<adresse>
<anrede bez="Frau"/>
<name>Hanna Mustermann</name>
<strasse>Seestr. 5</strasse>
<plz land="D">20000</plz>
<ort>Hamburg</ort>
</adresse>
<adresse>
<anrede bez="Firma"/>
<name>Demo</name>
<postfach>3042134</postfach>
<plz>10000</plz>
<ort>Berlin</ort>
</adresse>
</adressen>
Web-basierte Anwendungen
6. Juli 2015
90 / 295
Grundlagen Web
Bibliotheken
Frameworks – Beispiel django
django – Das Fullstack Framework
• Alles dabei was man braucht
• Objekt-Relationaler Mapper
• Admin-Oberfläche
Django is a high-level
Python Web framework
that encourages rapid
development and clean,
pragmatic design.
www.djangoproject.com
• Aussagekräftige URLs, Routing
• Templating
• Caching
• Internationalisierung
• ...
• Weit verbreitet
• Man ist sehr schnell sehr produktiv!
Alternativen
• wiki.python.org/moin/WebFrameworks, Dutzende
in Python
• Andere Sprachen: Ruby (on Rails), PHP (Zend), Java
(JavaEE, JSF), C (-)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
91 / 295
Grundlagen Web
Bibliotheken
Fazit – Grundlagen Web
Was passiert wirklich (in Frameworks)
• HTTP, CGI/WSGI, Request/Reponse-Zyklus
• Templating, Routing
• Sessions, Cookies
• Zustandsmanagement (Anwendungskontext) auf Client und Server
• Eine konkrete Middleware (werkzeug) aber noch kein Framework
• Integration von Datenbanken und anderer Bibliotheken
Verwendung
• Immer, wenn auch versteckt in Frameworks
• Immer, wenn man durchgreifen will oder muss
• Immer, wenn man bei der Fehlersuche verstehen will was passiert
Als nächstes
• Ein Framework (leider nicht django :-) und ein Container
• JavaEE Web Profile
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
92 / 295
JavaEE Web Tier
Servlet Container
Web-Anwendungen im Container
Wiederkehrende Probleme, unabhängig von der Anwendung
• Performance, Skalierbarkeit
• Verfügbarkeit
• Sicherheit
• Integration, zum Beispiel von Datenbanken
• Programmiermodelle
Ziel
• Abstraktion: Entwickler von wiederkehrenden Problemen entlasten
• Verbesserte Implementierung: Effiziente, skalierbare, . . . Ablaufumgebung
Lösungsansatz – Plattform
• Plattform abstrahiert/löst wiederkehrende Probleme
• Framework/APIs für Dienste
• Laufzeitumgebung (Container) in der Web-Anwendung läuft
• Festlegung Programmiermodell(e)
Beispiele: JavaEE, Grails, Play, Rails (Ruby), Django (Python), Zend (PHP), . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
93 / 295
JavaEE Web Tier
Servlet Container
JavaEE – Übersicht
JavaEE
• Java Enterprise Edition
• Aktuell Version 7 (6/13)
Version 6 verbreitet
• Plattform für Unter-
nehmensanwendungen
• Tonnen von APIs
• Container von
verschiedenen
Herstellern
• Mehrschichtenarchitektur
• http://docs.oracle.com/javaee/7/tutorial/
doc/img/overview-multitieredapps.gif, Fig. 1-1
Web Tier in dieser Veranstaltung
• Servlets – „CGI“ für Java
• Java Server Pages (JSP) – Template-Umgebung
• Java Server Faces (JSF) – Framework, Programmiermodell, Präsentation
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
94 / 295
JavaEE Web Tier
Servlet Container
JavaEE, Web Tier – Servlet Container
Servlet
• Java-Klasse, die Servlet-Schnittstelle implementiert
• Für Verarbeitung von (HTTP-)Anfragen
Servlet-Umgebung
• Laufzeitumgebung des Web Tier, Container
• Ausführen von Servlets, Übersetzen von JSP in
Servlets
• Weiterleiten einer Anfrage an passendes Servlet
• Session-Verwaltung
Tomcat
• (vormals) Referenzimplementierung, weit verbreitet
HTTP
Server
AJP
Connector
HTTP
Connector
• Aktuelle Version 8, wir verwenden 8.0.20
Servlet
Catalina
• JavaEE 7: Servlet 3.1, JSP 2.3
JSP
Jasper
• Ablaufumgebung Catalina, JSP-Compiler Jasper,
integrierter HTTP-Server
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
95 / 295
JavaEE Web Tier
Servlet Container
Tomcat Installieren und Verwenden
Archiv extrahieren
• In beliebigem Verzeichnis auspacken
• tar zxvf apache-tomcat-8.0.20.tar.gz
Konfiguration (conf) – nicht notwendig
Starten und Stoppen (bin)
• Starten: bin/startup.sh
HTTP-Server, Port 8080,
Servlet-Container, lokal
http://localhost:8080/
• Stoppen: bin/shutdown.sh
Logging (logs)
• logs/catalina.2015-05-19.log
• Anschauen!
Web-Anwendungen (webapps)
• Ihre Web-Anwendungen
• Spezielle Struktur
• Default: Beispiele, Doku
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
96 / 295
JavaEE Web Tier
Servlet Container
Web-Anwendungen im Container
Container
• Beherbergt mehrere
Web-Anwendungen
Web-Anwendung
• Besteht aus mehreren
Servlet-Klassen und anderen
Ressourcen
http://.../servlets/...
Laufzeit
• Für jede Anwendung ein
Servlet1
Servlet2
Servletkontext
Servletkontext
• Nur eine Instanz eines jeden
Servlets in einem Servletkontext
• Eine Instanz behandelt mehrere
Servlet1.class
Servlet
Servlet
Servlet
zur Laufzeit
statisch
Res1
Res1
Web-Anw1 (webtest)
Anfragen (auch parallel!)
Web-Anw2
Container
• (Sessions separat)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
97 / 295
JavaEE Web Tier
Servlet Container
Servlet – Lebenszyklus
Voraussetzung
Initialisierung
init()
• Klasse geladen, Instanz erzeugt
• Meist bei erster Anfrage
Initialisierung – init()
Request
Service
service(request,response)
• Eine Instanz für alle Anfragen
Response
• Einmal für Lebensdauer Container,
oder bei neu instanziieren
doGet(req,res)
doPost(req,res)
Service – service()
doPut(req,res)
• Weiterleitung
• Zuweisung Arbeits-Thread je Anfrage
...
• Ein Methodenaufruf je Anfrage
Freigabe – destroy()
• Am Ende der Lebensdauer Container,
oder vor neu instanziieren durch
Änderung Servlet
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
Freigabe
destroy()
6. Juli 2015
98 / 295
JavaEE Web Tier
Servlet Container
Servlet – Hello World
1
Framework-Idee
package webanw;
2
import javax.servlet.*;
import javax.servlet.http.*;
5 import java.io.*;
3
• Erben von vordefinierter Klasse
4
HttpServlet
6
7
• Statt Servlet
8
• Meist nur HTTP
9
10
doGet
11
12
• Statt überschreiben von service
13
14
• Default-Implementierung von service
15
public class HelloServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.print("Hello Servlet World");
}
}
ruft passend doGet, doPost, . . . auf
• Kein Rückgabewert
Parameter HttpServletRequest und HttpServletResponse
• HttpServletRequest: Instanz repräsentiert aktuelle Anfrage, stellt alle notwendigen
Informationen zur Verfügung
• HttpServletResponse: Instanz repräsentiert Ergebnis, erlaubt
Manipulation/Erstellen des Ergebnisses
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
99 / 295
JavaEE Web Tier
Servlet Container
Eingabe-Parameter, Enumeration
1
Zugriff auf Parameter
• getParameter(name) bei gegebenem
Namen
• getParameterNames() gibt Enumeration
vorhandener Namen zurück
Nur GET-Parameter
• Bei doPost die POST-Parameter
import
import
5 import
6 import
3
4
8
9
10
11
12
14
15
• Vorgabe ist text/html
16
17
• HTML-Rahmen ausgeben
Beispiel URL-Ende:
?name=Susi&var1=wert1
18
19
20
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
java.util.*;
7
13
Ausgabeformat
package webanw;
2
public class ParameterServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<html><body>");
String name = request.getParameter("name");
out.println("Hallo " + name + "<br />");
Enumeration<String> e =
request.getParameterNames();
while (e.hasMoreElements()) {
String key = e.nextElement();
String value = request.getParameter(key);
out.println(key + " = " + value + "<br />");
}
out.println("</body></html>");
}
}
Web-basierte Anwendungen
6. Juli 2015
100 / 295
JavaEE Web Tier
Servlet Container
Eingabe-Parameter, Map
Imports ab sofort weggelassen
Zugriff auf Parameter
1
2
• getParameterMap() gibt Map statt
Enumeration
3
zurück
4
• Schlüssel/Wert Paare, aber mehrere
Werte möglich (erlaubt bei HTTP)
5
6
7
8
• Verwenden erweiterte For-Schleife
9
10
möglich
11
12
Generics seit Servlet 3.0
13
• Bei Servlet 2.5 (noch weit verbreitet)
14
15
keine Typ-Spezifikation, expliziter Cast 16
17
notwendig
18
Beispiel URL-Ende:
?name=Susi&var1=wert1
19
20
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
public class ParameterServletMap extends HttpServlet
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<html><body>");
String name = request.getParameter("name");
out.println("Hallo " + name + "<br />");
Map<String, String[]> map =
request.getParameterMap();
for (String key : map.keySet()) {
String[] values = map.get(key);
out.println(key + " = ");
if (values.length == 1) {
out.println(values[0] + "<br />");
} else {
out.print("[ ");
for (String value : values) {
out.print(value + " ");
}
out.print("]");
}
}
out.println("</body></html>");
}
}
27
Web-basierte Anwendungen
6. Juli 2015
101 / 295
JavaEE Web Tier
Servlet Container
Weitere request-Methoden
• getCookies(): Feld mit allen
Cookies
• getHeader(name): Wert des
Headers mit Schlüssel name
• getHeaderNames(): Enumeration
der Schlüssel der Header
• getHeaders(): Enumeration der
Werte mit Schlüssel name
• getIntHeader(name): Wie
getHeader,
nur Rückgabe einer
ganzen Zahl (Konvertierung)
• ...
Dokumentation lesen: http://tomcat.apache.org/tomcat-8.0-doc/servletapi/
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
102 / 295
JavaEE Web Tier
Servlet Container
Ausgabe-Möglichkeiten
1
Zugriff Ausgabestrom
2
3
• getWriter() liefert PrintWriter-Instanz 4
5
• Verwenden wie System.out
6
7
Manipulieren der Antwort, Beispiele:
8
• Setzen des Inhaltstyps
9
10
• Setzen des Zeichensatz (Encoding)
11
12
• Abbruch Ausgabe, Fehlermeldung mit
HTTP-Statuscode 404
13
14
public class FehlerServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.print("Hello Feher Servlet World");
// Abbruch, Ausgabe/Einstellungen
// bis hier werden verworfen
response.sendError(404, "nix gefunden");
}
}
• ...
Weitere response-Methoden, Doku
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
103 / 295
JavaEE Web Tier
Servlet Container
Ausgabe Binärdaten
Button generieren
• Button mit „gut“ aussehendem
Text
1
2
3
4
5
6
• Generieren eines Bilds
7
8
• Bibliothek von Java,
Graphics2D
• Text als Parameter
9
10
11
12
13
Beispiele
14
15
• ...?
16
public class ButtonServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletOutputStream sos =
response.getOutputStream();
response.setContentType("image/png");
String text =
request.getParameter("text");
if (text == null) {
text = "Button";
}
BufferedImage img = renderButton(text);
ImageIO.write(img, "PNG", sos);
}
• ...?text=Ok . . .
• ServletOutputStream statt PrintWriter
• ImageIO von javax.imageio
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
104 / 295
JavaEE Web Tier
Servlet Container
Generieren Binärdaten
Bilder
• awt, awt.image
• Graphics2D-API
Alle Java Bibliotheken nutzbar
• Bilder
1
2
3
4
5
6
7
8
9
10
• Audio-, Video-Streaming
11
12
• PDF
13
14
• ...
15
16
17
18
19
20
21
22
23
24
25
Prof. Dr. Peter Barth (HS-RheinMain)
final static int WIDTH = 100;
final static int HEIGHT = 20;
public BufferedImage renderButton(String text) {
BufferedImage img =
new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.black);
g2d.drawRoundRect(0, 0, WIDTH-2, HEIGHT-2, 6, 6);
Font font = new Font(Font.SERIF, Font.PLAIN, 12);
g2d.setFont(font);
FontMetrics metrics = g2d.getFontMetrics(font);
g2d.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
int adv = metrics.stringWidth(text);
int hgt = metrics.getHeight();
g2d.drawString(text,
(WIDTH-adv)/2,
(HEIGHT/2)-2+(hgt/2));
g2d.dispose();
return img;
}
Web-basierte Anwendungen
6. Juli 2015
105 / 295
JavaEE Web Tier
Servlet Container
Weiterleitung – Server
1
• Ziel: Ein Quellcode für
GET und POST
• Weiterleitung der Anfrage im
Servlet Container
2
3
4
5
6
7
8
• Zeile 15, Methodenaufruf
9
10
11
GET
12
13
14
15
16
POST
Prof. Dr. Peter Barth (HS-RheinMain)
17
public class GetPostServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Das ist GET");
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Das ist zuerst POST");
doGet(request, response);
}
}
Web-basierte Anwendungen
6. Juli 2015
106 / 295
JavaEE Web Tier
Servlet Container
Weiterleitung – Client
1
• sendRedirect(url)
2
3
• Weiterleitung der Anfrage über
Client
4
5
6
• Der Client erhält eine spezielle
7
Antwort
• Der Client schickt daraufhin
eine neue Anfrage
8
9
10
public class RedirectServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String url = "HelloServlet";
url = response.encodeRedirectURL(url);
response.sendRedirect(url);
}
}
Web
Browser
• encodeRedirectURL(url)
berücksichtigt gegebenenfalls
Sessions
Hello
Servlet
Redirect
Servlet
redirect auf
HelloServlet
Nachteil
HTML von
HelloServlet
• Zwei Anfragen, aufwendig und langsam
Ziel
• Weiterleitung innerhalb des Containers
• Einfach und schnell
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
Web
Server
6. Juli 2015
107 / 295
JavaEE Web Tier
Servlet Container
Weiterleitung – Server, RequestDispatcher
Weiterleitung im Server an anderes
Servlet
1
2
3
4
• getRequestDispatcher(url),
URL-Pfad wie von außen
• Alternativ:
getNamedDispatcher,
servlet-name des Servlets
• forward(request, response),
5
6
7
8
9
10
11
12
13
Weiterleiten der Anfrage, Ersatz 14
15
der Ausgaben in out
public class WantNameServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
if (request.getParameter("name") != null) {
RequestDispatcher rd =
request.getRequestDispatcher("/ParameterServlet");
rd.forward(request, response);
} else {
PrintWriter out = response.getWriter();
out.println("Bitte ’name’ als GET-Parameter");
}
}
}
Web
Browser
• Alternativ: include,
Hinzufügen statt Ersatz
• Typisches Einsatzgebiet:
Controller (keine Ausgaben)
Beispiel: Falls GET-Parameter name
gesetzt, dann an ParameterServlet
weiterleiten, ansonsten Ausgabe
einer
Meldung
Prof. Dr. Peter Barth (HS-RheinMain)
WantName
Servlet
HTML von
ParameterServlet
Want
Name
Web-basierte Anwendungen
Parameter
Web
Server
6. Juli 2015
108 / 295
JavaEE Web Tier
Servlet Container
Struktur Web-Anwendung
Mapping?
• Woher weiß Tomcat was bei einem
Request (URL) passieren soll?
• Beispiel: /jee/HelloServlet
Struktur Webanwendung
• In eigenem Verzeichnis, beliebiger
Name, meist erster Teil der URL
• Statische Ressourcen (und JSP) auf
oberster Ebene, direkt zugreifbar
• Verzeichnis WEB-INF
• Nicht zugreifbar,
• Konfiguration in Datei web.xml
• Java .class-Dateien in classes
• Java .jar-Dateien (Libraries) in lib
• Inhalt von jee.war
• Alle Servlets in Java-Paket webanw
WAR-Datei (.war), Web Archive
• Paketierung Dateibaum als ZIP-Archiv
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
109 / 295
JavaEE Web Tier
Servlet Container
Deployment Web-Anwendung
Web-Anwendung „deployen“
• Web-Anwendung auf dem Container
bereit stellen
• Spezifisch je nach Container
Deployment mit tomcat
• Verzeichnisbaum der Web-Anwendung
unter webapps kopieren
• Sehr sinnvoll für Entwicklung
• Tomcat erkennt Änderungen
(meistens), notfalls neu starten
• WAR-Archiv unter webapps kopieren
• Tomcat packt Archiv aus
• Deployment-Manager Tools
verwenden
• Für Produktionsumgebungen sinnvoll,
siehe Dokumentation
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
110 / 295
JavaEE Web Tier
Servlet Container
Konfiguration mit web.xml
web.xml
Mapping
1
• Zuordnung von URL auf Servlet
• 1. Servlets erhalten Namen
• 2. URL-Pattern auf Namen
mappen
2
3
4
5
6
7
8
9
Beispiel
10
• Ziel: Anfrage
/jee/HelloServlet auf
Servlet-Klasse
webanw.HelloServlet
13
14
15
Web-App als erster Teil der URL
• Verbleibendes Ziel: Anfrage
/HelloServlet auf
• XML-Datei mit Schema (nicht abtippen,
ausnahmsweise Copy&Paste ok, aber nicht
von diesem Foliensatz (Zeichensatz))
• Erst Namen vergeben mit <servlet>
• Dann URL-Pattern auf einen Namen (damit
Servlet-Klasse
webanw.HelloServlet
12
mappen
• Konvention Tomcat, Unterordner
Prof. Dr. Peter Barth (HS-RheinMain)
11
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>webanw.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
</web-app>
mappen
Servlet) mappen mit <servlet-mapping>
Web-basierte Anwendungen
6. Juli 2015
111 / 295
JavaEE Web Tier
Servlet Container
Wichtigste Tags web.xml
servlet-name:
• Eindeutiger Name für jedes
Servlet
servlet-mapping:
• Zuordnung von ein oder
mehreren URL-Patterns an ein
Servlet (per Name)
• Mehrere URLs können auf ein-
und dasselbe Servlet gemappt
werden
• Achtung: In Beispielen wurde
bisher immer der Klassenname
des Servlets auf das jeweilige
Servlet gemappt – nur für
Beispiele
url-pattern
– Möglichkeiten rechts
Prof. Dr. Peter Barth (HS-RheinMain)
• Exaktes Pattern: /<pfad>
• Start mit /, danach beliebiger Pfad <pfad>
• Ein exakter Pfad für ein Servlet
• /HelloServlet
http://.../jee/HelloServlet
• Erweiterungs-Pattern: *.<ext>
• Alle Pfade mit spezifischer Endung <ext> bei
letzter Pfadkomponente
• Nur ein Stern am Anfang erlaubt
• Intern: *.jsp auf Jasper
• *.rst
http://.../jee/doku/toll/intro.rst
• Pfad-Pattern: /<pfad>/*
• Alle Pfade mit festem Anfang /<pfad>
• Matcht alles „unterhalb“
• Nur ein Stern am Ende erlaubt, beginnt mit /
• /foo/bar/*
http://.../jee/foo/bar/hallo/welt
Web-basierte Anwendungen
6. Juli 2015
112 / 295
JavaEE Web Tier
Servlet Container
Weitere Tags web.xml
welcome-file-list
session-config
• Liste von Dateien in absteigender
Priorität, die ohne weitere URI-Angabe
im Kontext der Web-Anwendung
angezeigt werden
• Beispiel: Anzeige von welcome.html,
wenn nicht vorhanden, dann
index.html
<welcome-file-list>
<welcome-file>welcome.html</welcome-file>
3
<welcome-file>index.html</welcome-file>
4 </welcome-file-list>
1
2
• Konfiguration von Sessions
mime-mapping
• Welche Datei-Erweiterung liefert
welchen Mime-Typ
error-page
• Welche Seite bei welchem Fehlercode
... und vieles mehr
• Wird bei Bedarf ergänzt
• Vollständige Referenz in der
context-param
• Parameter für die gesamte
Dokumentation
• Servlet-API 3.0, JSR 315
Web-Anwendung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
113 / 295
JavaEE Web Tier
Servlet Container
Annotationen ab Servlet 3.0
1
Konfiguration mit web.xml
2
3
• XML, verbose, komplex
4
• Trennung von Konfiguration und Code
(prinzipiell gut, aber aufwendig)
5
6
7
8
• Häufig muss Mapping nicht
9
konfigurierbar sein, beziehungsweise
ein Default-Mapping wäre schön
Default-Konfiguration im Code mit
Annotationen
10
<web-app ...>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>webanw.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet></servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
package webanw;
import javax.servlet.*;
3 import javax.servlet.http.*;
4 import java.io.*;
5 import javax.servlet.annotation.*;
1
2
• Seit Servlet 3.0
6
• Annotation WebServlet mit name und
urlPatterns
@WebServlet(name="webanw.HelloServlet",
urlPatterns={"/hello"})
9 public class HelloServlet extends HttpServlet {
10
...
11 }
7
8
• Annotationsverarbeitung abschaltbar
wenn nicht benötigt
1
2
Prof. Dr. Peter Barth (HS-RheinMain)
<web-app ...
metadata-complete="true">
Web-basierte Anwendungen
6. Juli 2015
114 / 295
JavaEE Web Tier
Servlet Container
Weitere Annotationen ab Servlet 3.0
• @WebFilter: Anlegen eines Filters
Konventionen in Veranstaltung
• Wir verwenden einheitlich die
Konfiguration in web.xml
• @WebInitParam: Initialisierungs-
parameter von Servlet/Filter
• Kompatibilität zu Servlet 2.5
• Konfiguration bleibt unabhängig von
• @WebListener: Event-Listener
Source-Code
• @MultiPartConfig: Servlet mit
• Keine weiteren Neuerungen von
Mime-Attachments
Servlet 3.0
• ...
• Fragmente zur Inklusion in web.xml
• Asynchrone Anfragebearbeitung
• ...
• Doku für Details, Parameter, etc.
Annotation versus web.xml
• Einträge in web.xml überschreiben
Konfiguration per Annotation
• Einfach bei der Entwicklung, sinnvolle
Vorgabewerte ohne XML
• Nachträgliche Konfiguration ohne
Code-Änderung bei Deployment
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
115 / 295
JavaEE Web Tier
Servlet Container
Beispiel – Konfiguration eines Servlets mit web.xml
Ziel – Deployment Parameter
• Servlet konfigurierbar
• Parameter ändern ohne
Quellcode-Anpassung oder neu
übersetzen
• Ändern der web.xml
1
2
3
4
5
6
7
8
9
10
11
public class GrussServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletConfig sc = getServletConfig();
String gruss = sc.getInitParameter("gruss");
PrintWriter out = response.getWriter();
out.print(gruss + " Welt!");
}
}
ServletConfig
• Zugriff auf Konfiguration
• Spezifisch für Servlet
• getInitParameter zum Zugriff
auf Parameter
1
2
3
4
5
6
7
init-param
• Innerhalb Servlet-Tag
• Name/Wert Paare
8
9
10
11
12
<servlet>
<servlet-name>GrussServlet</servlet-name>
<servlet-class>webanw.GrussServlet</servlet-class>
<init-param>
<param-name>gruss</param-name>
<param-value>Guude</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>GrussServlet</servlet-name>
<url-pattern>/GrussServlet</url-pattern>
</servlet-mapping>
• Mehrere möglich
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
116 / 295
JavaEE Web Tier
Servlet Container
Beispiel – Konfiguration Datenbankzugriff, Zugriff
Datenbankverbindung als
Instanzvariable
1
2
3
4
• In init initialisiert
5
6
• Für alle Anfragen während der
Lebensdauer des Servlets
verfügbar
• Skaliert nicht gut, fix gleich . . .
7
8
9
10
11
12
Logging
13
14
• Mit log, in log/localhost...
15
16
• Stacktrace zeigen
17
18
19
20
21
22
23
24
25
26
27
Prof. Dr. Peter Barth (HS-RheinMain)
public class DBServlet extends HttpServlet {
private Connection con = null; // in init
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.print("<html><body><h3>DB-Benutzer</h3>");
String query = "SELECT usename, usesysid FROM pg_user";
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
out.println("<table>");
out.print("<tr><th>usename</th>");
out.println("<th>usesysid</th></tr>");
while (rs.next()) {
String usename = rs.getString("usename");
String usesysid = rs.getString("usesysid");
out.print("<tr><td>"+usename+"</td>");
out.print("<td>"+usesysid+"</td></tr>");
}
out.println("</table>");
} catch (SQLException e) {
out.println("DB-Fehler");
log("SQL-Fehler mit Anfrage " + query, e);
}
out.println("</body></html>");
}
28
Web-basierte Anwendungen
6. Juli 2015
117 / 295
JavaEE Web Tier
Servlet Container
Beispiel – Konfiguration Datenbankzugriff, Initialisierung
Treiber laden
• JAR-Datei des Treibers in das
Verzeichnis WEB-INF/lib
kopieren, machen wir immer so
• Bei Produktivbetrieb auch in lib
des Containers möglich
1
2
3
4
5
6
7
8
9
10
11
Konfiguration
• ServletConfig
• Parameter in web.xml
1
2
3
4
5
6
7
<init-param>
<param-name>dbhost</param-name>
<param-value>
db.intern.mi.hs-rm.de
</param-value>
</init-param>
<!-- ... -->
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
@Override
public void init() {
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
log("Postgresql driver did not load");
return;
}
ServletConfig sc = getServletConfig();
String dbhost = sc.getInitParameter("dbhost");
String dbname = sc.getInitParameter("dbname");
String dbuser = sc.getInitParameter("dbuser");
String dbpassword = sc.getInitParameter("dbpassword");
if (dbhost == null || dbname == null ||
dbuser == null || dbpassword == null) {
log("Not all parameters for database set");
return;
}
String url = "jdbc:postgresql://"+dbhost+":5432/"+dbname;
try {
con = DriverManager.getConnection(url, dbuser, dbpasswor
} catch (SQLException e) {
log("Could not connect");
return;
}
}
Web-basierte Anwendungen
6. Juli 2015
118 / 295
JavaEE Web Tier
Servlet Container
Instanzvariablen? – Achtung!
Instanzvariable
Threadsicherheit
• Eine offene Datenbankverbindung als
Instanzvariable im Servlet
• Alle Anfragen gegen eine Verbindung
• Initialisierung bei Erstellung Servlet
• Es gibt nur eine Instanz des Servlets
• Funktioniert nur, wenn der
JDBC-Treiber threadsicher ist
• Ok bei PostgreSQL
• Aber Serialisierung der Anfragen
für alle Benutzer/Anfragen
Probleme
• Threadsicherheit: Mehrere Threads
(Container) greifen gleichzeitig auf
Datenbank zu
• Skalierung: Mehrere Threads des
Containers greifen über eine
Verbindung auf Datenbank zu
Skalierung
• Datenbankverbindung wird Engpass
• Lösung: eine Datenbankverbindung je
Thread, ThreadLocal
Instanzvariablen vermeiden
• Meist falsch eingesetzt (Lebenszyklus)
Instanzvariablen
• Ziel: Aufwand je Anfrage verringern
• Beispiel: Verbindungsaufbau zur DB
Prof. Dr. Peter Barth (HS-RheinMain)
• Nur eine Servlet-Instanz (nicht verteilt)
• Meist anderer Scope richtig
• Korrekte Nutzung komplex
Web-basierte Anwendungen
6. Juli 2015
119 / 295
JavaEE Web Tier
Servlet Container
Datenbankzugriff – Verbindung je Anfrage
Eine neue Datenbankverbindung je
Anfrage
1
2
3
4
• Verbindung aufmachen
5
6
• Verbindung nutzen
7
8
• Verbindung schließen
Auslagern in DBServletUtil
• getConnection(ServletConfig):
9
10
11
12
13
public class DBServletPerRequest extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletConfig sc = getServletConfig();
Connection con = DBServletUtil.getConnection(sc);
PrintWriter out = response.getWriter();
out.print(DBServletUtil.genHTML(con, "PerRequest"));
try {
con.close();
} catch (SQLException ignore) { }
}
Holt sich eine neue
Datenbankverbindung
• genHTML(Connection): Generiert
die Ausgabe (Tabelle der
Benutzer) bei gegebener
Datenbankverbindung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
120 / 295
JavaEE Web Tier
Servlet Container
Datenbankzugriff – Verbindung je Thread
Idee
• Eine Datenbankverbindung je
Thread
1
2
3
4
5
6
• Java Feature ThreadLocal
7
8
Aufbau
• Bei Anfrage
• Wenn für Thread noch keine
Verbindung, dann öffnen und
merken
Abbau
• Am Ende der
Servlet-Lebensdauer
• Alle Verbindungen schließen
• Treiber entladen, sonst
Speicherleck
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Prof. Dr. Peter Barth (HS-RheinMain)
public class DBServletPerThread extends HttpServlet {
private ThreadLocal<Connection> tcon =
new ThreadLocal<Connection>();
private List<Connection> cons =
new ArrayList<Connection>();
public Connection getConnection() {
if (tcon.get() == null) {
ServletConfig sc = getServletConfig();
tcon.set(DBServletUtil.getConnection(sc));
synchronized (cons) { cons.add(tcon.get()); }
}
return tcon.get();
}
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
Connection con = getConnection();
out.print(DBServletUtil.genHTML(con, "PerThread"));
}
@Override public void destroy() {
try {
for (Connection con: cons) { con.close(); }
Driver driver = DriverManager.getDriver("jdbc:postgres
DriverManager.deregisterDriver(driver);
} catch (SQLException ignore) { }
}
28
Web-basierte Anwendungen
6. Juli 2015
121 / 295
JavaEE Web Tier
Servlet Container
Datenbankzugriff – Übersicht
Mini-Benchmark
Optionen
• Eine Verbindung je Anfrage: Früher
sehr teuer (bei teurem
Verbindungsaufbau), heute
akzeptabel, skaliert nicht gut
• 42 Benutzer gleichzeitig, so schnell
wie möglich (-b) für 10 Sekunden
• Eine Verbindung: Wiederverwendung
der Verbindung, effizient, aber skaliert
nicht, potentiell anfällig für
Nebenläufigkeit, vermeiden
(Instanzvariable, Anti-Pattern)
• Eine Verbindung je Thread: Sehr guter
Match von notwendigen und zur
Verfügung gestellten Verbindungen,
effizient und skaliert, etwas komplex
zu realisieren
Prof. Dr. Peter Barth (HS-RheinMain)
• siege -b -c 42 -t 10s
http://localhost:8080/jee/...
• Wieviele Anfragen wurden geschafft?
• Achtung, nur einfache cachebare
Anfrage, nicht aussagekräftig
• Lies, damn lies, statistics, benchmarks
Option
One per request
One
One per thread
Web-basierte Anwendungen
Anzahl Anfragen
1529
12130
19890
6. Juli 2015
122 / 295
JavaEE Web Tier
Servlet Container
Scopes – Übersicht
Achtung: Anfragen potenziell parallel
Synchronisation, wenn notwendig, Aufgabe des Entwicklers
Attr.
Anfrage
Sessionattribut
Session
Servlet
Servlet
forward
Servlet
Instanzvariable*
Attribut
ServletContext
Anwendung
Lebensdauer Anwendung
* zum Beispiel für Caching, Datenverbindung, …
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
123 / 295
JavaEE Web Tier
Servlet Container
Anfrage-Scope
Anfrage-Scope
• Objekt für die Dauer einer
Anfrage vorhalten
1
2
3
4
5
6
• request.setAttribute,
request.getAttribute
• Länger als Block
7
8
9
10
11
public class Anfrage1Servlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("answer", "fortytwo");
RequestDispatcher rd =
request.getRequestDispatcher("/Anfrage2Servlet");
rd.forward(request, response);
}
}
(Programmiersprache)
1
2
3
4
5
6
7
8
9
10
Prof. Dr. Peter Barth (HS-RheinMain)
public class Anfrage2Servlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
String wert = (String) request.getAttribute("answer");
out.println("answer = " + wert);
}
}
Web-basierte Anwendungen
6. Juli 2015
124 / 295
JavaEE Web Tier
Servlet Container
Session-Scope
1
Session-Scope
2
• HttpSession nur mit HTTP verfügbar
• request.getSession() assoziiert mit
aktueller Anfrage, erzeugt neue
Session falls noch keine vorhanden
3
4
5
6
7
8
9
10
• Achtung: Bei Links in gleiche
11
Web-Anwendung encodeURL
12
verwenden (URL-Rewriting, Anhängen13
14
Session-ID)
15
• Vorteil gegenüber zum Beispiel
werkzeug; Ablage im Hauptspeicher
16
17
18
public class SessionServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
Object o = session.getAttribute("zaehler");
Integer zaehler = (Integer) o;
if (zaehler == null) {
zaehler = Integer.valueOf(0);
}
zaehler += 1;
session.setAttribute("zaehler", zaehler);
PrintWriter out = response.getWriter();
out.print("Session " + zaehler);
out.println(" mal geklickt");
}
}
(JVM), kein (De-)Serialisieren
Attribute: get/setAttribute,
getAttributeNames, removeAttribute
Lebensdauer Session:
get/setMaxInactiveInterval, isNew,
getCreationTime, getLastAccessedTime
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
125 / 295
JavaEE Web Tier
Servlet Container
Anwendungs-Scope
1
Anwendungs-Scope
2
3
• Objekte leben während der
4
Lebensdauer der Anwendung
5
6
• Instanzvariable Servlet kann kürzer
sein, und nur von Servlet zugreifbar
7
8
9
10
ServletContext
11
• getServletContext()
12
13
• Für alle Servlets gleiches Objekt
14
15
public class Anwendung1Servlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletContext app = getServletContext();
Object o = app.getAttribute("zaehler");
Integer zaehler = (Integer) o;
if (zaehler == null)
zaehler = Integer.valueOf(0);
zaehler += 1;
app.setAttribute("zaehler", zaehler);
PrintWriter out = response.getWriter();
out.println("App1 wurde geklickt");
}
}
• (im Gegensatz zu getServletConfig,
nur dieses Servlet, nur lesen)
1
Attribute: get/setAttribute,
2
getAttributeNames removeAttribute
siege -b -c 1 -r 17
http://.../Anwendung1Servlet
3
4
5
6
7
8
9
10
Prof. Dr. Peter Barth (HS-RheinMain)
public class Anwendung2Servlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletContext app = getServletContext();
int z = (Integer) app.getAttribute("zaehler");
PrintWriter out = response.getWriter();
out.println("App1-Klicks: " + z);
}
}
Web-basierte Anwendungen
6. Juli 2015
126 / 295
JavaEE Web Tier
Servlet Container
Anwendungs-Scope – Synchronisiert
1
Threadsicherheit
2
• Code letzte Folie nicht threadsicher
• Kritischer Wettlauf bei Aktualisierung
zaehler
3
4
5
6
7
8
Synchronisation
9
• AtomicLong, threadsicherer Zähler in
10
11
12
java.util.concurrent.atomic
13
• Initialisierung in init, Garantiert noch
14
15
kein (nebenläufiger) Zugriff
16
• incrementAndGet garantiert atomar
1
• Ausgabe nur lesen
2
3
siege -b -c 100 -r 17
4
http://.../Anwendung1Servlet
5
6
7
8
9
10
Prof. Dr. Peter Barth (HS-RheinMain)
public class Anwendung1ServletTS extends HttpServlet
public void init() {
AtomicLong al = new AtomicLong(0);
getServletContext().setAttribute("al", al);
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletContext app = getServletContext();
AtomicLong al =
(AtomicLong) app.getAttribute("al");
al.incrementAndGet();
PrintWriter out = response.getWriter();
out.println("App1 wurde geklickt");
}
}
public class Anwendung2ServletTS extends HttpServlet
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
ServletContext app = getServletContext();
AtomicLong al =
(AtomicLong) app.getAttribute("al");
PrintWriter out = response.getWriter();
out.println("App1-Klicks: " + al.get());
}
}
11
Web-basierte Anwendungen
6. Juli 2015
127 / 295
JavaEE Web Tier
Servlet Container
ServletContext
<context-param>
<param-name>admin</param-name>
3
<param-value>nobody in his right mind</param-value>
4 </context-param>
1
getInitParameter
2
• Zugriff auf Parameter definiert in
web.xml
• Definition auf web-app Ebene,
1
unabhängig von Servlets
2
ServletContext app = getServletContext();
String admin = app.getInitParameter("admin");
getResourceAsStream
• Zugriff auf zum Beispiel statische
1
2
ServletContext app = getServletContext();
String admin = app.getInitParameter("admin");
Inhalte wie Bilder oder ähnliches
• Liegen nicht unbedingt im
Dateisystem, sondern in einer
WAR-Datei (Zip-Archiv)
NIE auf Dateisystem im web-app Pfad
versuchen zuzugreifen
java.io.InputStream is;
// ab Wurzelverzeichnis der web-app
3 // URL wäre .../<web-app>/images/hsrm.png
4 is = app.getResourceAsStream("/images/hsrm.png");
5 BufferedImage img = ImageIO.read(is);
1
2
log
• Eintrag in Log-Datei
• Optionaler zweiter Parameter
Throwable
für Stacktrace
1
log("Alles klar");
weiteres in der Dokumentation
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
128 / 295
JavaEE Web Tier
Servlet Container
Verhalten anpassen – Servlet Filter
Problem – Einheitliches Verhalten für verschiedene Servlets
• Jedes Servlet muss dies separat implementieren
• Wunsch ist konfigurierbares Verhalten
• Beispiel: Bilder komprimieren, loggen und tracen, validieren, authentifizieren,
dispatchen, Konvertierung, . . .
Lösung – Servlet Filter
• Anfrage und Antwort durchläuft eine konfigurierbare Serie von Filtern
• Filter (Java-Klasse) kann Inhalte verändern, Anfragen verändern/blocken, . . .
Request
Filter
Filter
Filter
Servlet
Response
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
129 / 295
JavaEE Web Tier
Servlet Container
Filter Erstellen
Filter
1
2
• Schnittstelle, für einen konkreten 34
Filter zu implementieren
5
6
• init: Direkt nach Erstellung
• doFilter: Anfragebearbeitung,
weiter in der Filterkette mit
7
8
9
10
11
chain.doFilter
• destroy: Am Ende der
12
13
web.xml
Lebenszeit des Filters
• Lebenszyklus wie bei Servlets
Konfiguration
1
2
3
4
• in web.xml
5
• <filter>, <filter-mapping>
7
• Wie bei Servlets
9
Prof. Dr. Peter Barth (HS-RheinMain)
public class MachtNixFilter implements Filter {
public void init(FilterConfig filterConfig) { }
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
// hier Anfrage manipulieren
// request.getServletContext().log("MachtNixFilter");
chain.doFilter(request, response);
// hier Antwort manipulieren
}
public void destroy() { }
}
6
8
<filter>
<filter-name>MachtNix</filter-name>
<filter-class>webanw.MachtNixFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MachtNix</filter-name>
<url-pattern>/*</url-pattern>
<!-- Alternativ mit Servletnamen -->
</filter-mapping>
Web-basierte Anwendungen
6. Juli 2015
130 / 295
JavaEE Web Tier
Servlet Container
Antwort Aufsammeln zur Manipulation
public class ResponseWrapper extends
HttpServletResponseWrapper {
3
private CharArrayWriter buffer;
1
Ausgabe abfangen und aufsammeln
2
• Ein Objekt, das sich wie eine Antwort
verhält
4
public ResponseWrapper(
HttpServletResponse response) {
super(response); // default durchleiten
buffer = new CharArrayWriter();
}
5
6
• Ein Objekt, das (noch) nichts an den
7
8
Ausgabestrom zurück gibt
9
10
HttpServletResponseWrapper
// Verwender erhält Fake, keine richtige Antwort
public PrintWriter getWriter() {
return new PrintWriter(buffer);
}
11
• Klasse um eine Antwort (response)
12
13
einzupacken
14
15
• Verwenden durch erben und
// Filter kann Inhalt auslesen, manipulieren
// und weitergeben
public String getContent() {
return buffer.toString();
}
16
Methoden anpassen
17
18
Beispiel
19
20
• In Puffer schreiben
21
}
• Später auslesen
• Nur für Zeichen, Byte-Ströme separat
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
131 / 295
JavaEE Web Tier
Servlet Container
Beispielmanipulation – Fette Medieninformatik
1
Medieninformatik fett machen
2
3
• Unverändertes Weiterleiten der
4
Anfrage
5
6
• Auslesen der Antwort
7
8
• Jedes Vorkommen von
Medieninformatik mit
<strong>-Tag umrahmen
9
10
11
12
• Manipulationsergebnis in erhaltene
Antwort schicken
13
14
15
16
17
18
19
20
21
public class MIFilter implements Filter {
public void init(FilterConfig filterConfig) { }
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletResponse httpres =
(HttpServletResponse) response;
ResponseWrapper wrap =
new ResponseWrapper(httpres);
chain.doFilter(request, wrap);
PrintWriter out = response.getWriter();
for (String s: wrap.getContent().split("[\r\n]+")
s = s.replace("Medieninformatik",
"<strong>Medieninformatik</strong>");
out.println(s +"\n");
}
out.close();
}
public void destroy() { }
}
<html><body>
Die Medieninformatik ist cool. <br />
3 Die Medieninformatik ist die Beste. <br />
4 Medien-informatik ist nicht Medieninformatik
5 </body></html>
1
2
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
132 / 295
JavaEE Web Tier
Servlet Container
Filterketten Konfigurieren
Mehrere Filter für ein Servlet/JSP/... konfiguriert
• Reihenfolge ist die Reihenfolge der passenden <filter-mapping>-Einträge in
web.xml
• Erst die url-pattern-Mappings, dann die servlet-name-Mappings
Ausführung automatisch
• Durch Aufruf von doChain der Klasse FilterChain
• FilterChain-Instanz wird von Container bereitgestellt
/seite.bla
/p1/s.bla
/p1/i.html
/*
Prof. Dr. Peter Barth (HS-RheinMain)
*.bla
Web-basierte Anwendungen
/p1/*
6. Juli 2015
133 / 295
JavaEE Web Tier
Servlet Container
Hinweis für Entwicklung – Vermeiden von Caching
Während Entwicklung
Seite bleibt alt
• Auf lokalem Rechner arbeiten
• Code (HTML-Seite, Bilder, . . . )
geändert, aber alte Seite/Fehler wird
noch angezeigt
• Nur Tomcat und Browser als
Infrastrukturkomponenten
• Grund: Irgendwelche Infrastruktur-
komponenten (Browser, Proxy, Cache,
. . . ) versuchen erfolgreich zu cachen
Cachen ausreden
• Versuchen mit Header-Informationen
die Infrastrukturkomponenten zu
überzeugen nicht zu cachen
• Ein möglicher Versuch anbei
Prof. Dr. Peter Barth (HS-RheinMain)
1
2
3
4
5
6
response.addHeader("Pragma", "no-cache");
response.setHeader("Cache-Control",
"no-cache, no-store, must-revalidate");
response.addHeader("Cache-Control",
"pre-ceck=0, post-check=0");
response.setDateHeader("Expires", 0);
Web-basierte Anwendungen
6. Juli 2015
134 / 295
JavaEE Web Tier
Servlet Container
Eclipse-Unterstützung – Web Tools Platform (WTP)
Eclipse-Unterstützung
• Sinnvoll für JavaEE
• Trotzdem web.xml,
etc. manuell erstellen
und konfigurieren
• Vermeide Wizards
(macht blöd),
nutze Vervollständigung
(vermeidet auswendig
lernen)
Features
• Struktur vorgegeben
• Vervollständigung
• Templates (Java, JSP, . . . )
• Servlet-Engine (Tomcat) Integration
Screencasts: http://www.mi.hs-rm.de/˜barth/hsrm/webanw/info
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
135 / 295
JavaEE Web Tier
Java Server Pages
Templating mit Java Server Pages (JSP)
Web-Entwicklung mit Servlets
Initialisierung
jspInit()
• Grundlegende Technik, Probleme wie CGI
• Schwierig korrektes Markup zu erzeugen
• Tendenz zu Spaghetti-Code
Request
Java Server Pages (JSP)
Response
• Templating für JavaEE
Service
jspService(
request,
response)
• Fokus auf Präsentationsschicht
• Nicht Ersatz, sondern Ergänzung zu Servlets
JSP Umsetzung
• Laufzeitumgebung ist Servlet-Container, zum
Beispiel Tomcat
• JSP werden vor Ausführung in Servlets übersetzt,
zum Beispiel mit Jasper
Freigabe
jspDestroy()
• Lebenszyklus wie bei Servlet
• Andere Methoden, nicht direkt verwendet
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
136 / 295
JavaEE Web Tier
Java Server Pages
Hello JSP World
hello_jsp.java
Struktur
1
2
• Markup/HTML
3
• Tags in <% ... %> für Java-Code, in
<%= ... %> für Wertausgabe
4
5
6
7
Integration in Container
8
9
• Ablage innerhalb Web-App auf
10
oberster Ebene, wie statischer Inhalt
(nicht unterhalb WEB-INF)
• Wird automatisch bei Änderung von
Container in Servlet übersetzt und
automatisch „deployed“
hello.jsp
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
<html>
<head><title>Hello World</title></head>
<body>
Hallo Welt <br />
<%= new java.util.Date() %>
</body>
</html>
Prof. Dr. Peter Barth (HS-RheinMain)
21
22
23
24
25
26
...
public final class hello_jsp extends org.apache.jaspe
implements org.apache.jasper.runtime.JspSourceDepe
...
public void _jspService(final javax.servlet.http.Htt
throws java.io.IOException, javax.servlet.Servl
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context =
try {
response.setContentType("text/html");
pageContext = _jspxFactory.getPageContext(this, r
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("<html>\n");
out.write("<head><title>Hello World</title></head
out.write("<body>\n");
27
Web-basierte Anwendungen
6. Juli 2015
137 / 295
JavaEE Web Tier
Java Server Pages
JSP Scripting Tags
Scriptlets
<% ... %>
• Beliebiger Java Code, mehrere Anweisungen
• Mit Semikolon jede Anweisung abschließen
• Wird direkt in die Servlet-Methode _jspService übernommen
Ausdrücke
<%= ... %>
• Nur ein Java Ausdruck, keine Anweisungen, kein Semikolon am Ende
• Wert des Ausdrucks wird im Markup ausgegeben
• Wird in _jspServive in out.print Methodenaufruf übernommen
Deklarationen
<%! ... %>
• Beliebiger Java Code, Methoden und Variablen Deklarationen
• Wird in die Servlet-Klasse übernommen
Direktiven
<%@ ... %>
• Nachrichten an den Servlet-Container
• Definition von Seiten-Attributen, inkludieren von Seiten, . . .
• Wird in die Datei der Servlet-Klasse außerhalb des Servlets übernommen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
138 / 295
JavaEE Web Tier
Java Server Pages
Scriptlet, Beispiel
quadratzahlen.jsp
Java Code innerhalb von <% ... %>
1
• Beliebiger Code, der innerhalb einer
Methode erlaubt ist
2
3
4
5
• Vordefinierte Variablen, viele, von
Servlet
bekannt, spart Code
6
7
8
• PrintWriter out
• HttpServletRequest request
• HttpServletResponse response
• ServletConfig config
• ServletContext application
• HttpSession session
Achtung: Verleitet zu bösem
Spaghetti-Code
Prof. Dr. Peter Barth (HS-RheinMain)
9
10
11
12
13
14
15
16
17
<html><body>
<h3> Quadratzahlen </h3>
<table>
<tr><th>Zahl</th><th>Quadratzahl</th></tr>
<%
int wieviel = 9;
String p = request.getParameter("wieviel");
if (p != null) {
wieviel = Integer.parseInt(p);
}
for (int i=1; i <= wieviel; i+=1) {
out.print("<tr><td>"+i+"</td>");
out.print("<td>"+(i*i)+"</td></tr>");
}
%>
</table>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
139 / 295
JavaEE Web Tier
Java Server Pages
Ausdrücke, Beispiel
Java Ausdruck innerhalb von <%= ... %>
• Ein Java-Ausdruck der zu einem Wert
evaluiert
1
2
3
4
5
6
• Wird mit toString() zu String
7
8
konvertiert, wenn notwendig
9
10
• Korrekte Konvertierung von
11
Grundtypen
12
13
• Ausgabe mit out.print
14
15
• Nutzen vordefinierter Variablen
16
möglich
17
<html><body>
<h3> Quadratzahlen </h3>
<table>
<tr><th>Zahl</th><th>Quadratzahl</th></tr>
<%
int wieviel = 9;
String p = request.getParameter("wieviel");
if (p != null) {
wieviel = Integer.parseInt(p);
}
for (int i=1; i <= wieviel; i+=1) {
%>
<tr><td><%= i %></td>
<td><%= i*i %></td></tr>
<% } %>
</table>
</body></html>
Verwendung
• Markup kann im Editor erstellt und
überprüft werden
• Wird meist zusammen mit Scriptlets
verwendet
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
140 / 295
JavaEE Web Tier
Java Server Pages
Deklaration, Beispiel
Java Deklaration innerhalb <%! ... %>
• Beliebiger Methoden und Variablen
Deklarationen
1
2
3
4
5
6
• Vordefinierte Variablen der Anfrage
stehen natürlich nicht zur Verfügung
7
8
9
10
Verwendung
11
• Deklaration spezifischer Methoden,
12
13
Code Strukturierung
14
• Achtung: Instanzvariablen nicht zum
Merken eines Session-Zustands
verwenden
15
16
17
18
19
• Instanzvariable ist nur für
20
Servlet-Instanz gültig, was keinem
sinnvollen Scope entspricht
Prof. Dr. Peter Barth (HS-RheinMain)
21
<%!
final static int DEFAULT_WIEVIEL = 9;
int getWieviel(HttpServletRequest request) {
String p = request.getParameter("wieviel");
if (p != null)
return Integer.parseInt(p);
return DEFAULT_WIEVIEL;
}
%>
<html><body>
<h3> Quadratzahlen </h3>
<table>
<tr><th>Zahl</th><th>Quadratzahl</th></tr>
<%
for (int i=1; i <= getWieviel(request); i+=1) {
%>
<tr><td><%= i %></td>
<td><%= i*i %></td></tr>
<% } %>
</table>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
141 / 295
JavaEE Web Tier
Java Server Pages
Quadratzahlen Pager mit Session
1
Sessioninhalte in Session
2
3
• Bei Neuladen die nächsten 9
4
Quadratzahlen
5
6
• Wie weit ist Session-spezifisch, in
Session
merken
7
8
9
• Nicht in den Instanzvariablen des
10
11
Servlets merken
12
• session vordefiniert, einfach nutzen
13
14
15
16
Prof. Dr. Peter Barth (HS-RheinMain)
<html><body>
<h3> Quadratzahlen </h3>
<table>
<tr><th>Zahl</th><th>Quadratzahl</th></tr>
<%
Integer akt = (Integer) session.getAttribute("akt");
akt = (akt == null) ? 1 : akt;
for (int i=akt; i < akt+9; i+=1) {
%>
<tr><td><%= i %></td>
<td><%= i*i %></td></tr>
<% }
session.setAttribute("akt", akt + 9);
%>
</table>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
142 / 295
JavaEE Web Tier
Java Server Pages
Kommentare
comment.jsp
Kommentare innerhalb <%-- ... --%>
1
2
• Beliebiger Text innerhalb des
3
Kommentars
4
5
• Der Kommentar wird nicht im Markup
6
generiert
7
Verwendung
8
9
• Reduktion der Netzwerklast, kleinere
10
Seite
• Keine potenziell kritischen
Informationen übermitteln
<html>
<body>
<!-- HTML-Kommentar, sieht man im Markup -->
<%-- JSP-Kommentar, sieht man im Markup nicht --%>
Hallo HTML Welt <br />
<%
out.println(" Hallo JSP
Welt <br />"); // Java Kommentar
%>
</body>
</html>
http://.../comment.jsp
<html>
2 <body>
3
<!-- HTML-Kommentar, sieht man im Markup -->
1
4
5
6
Hallo HTML Welt <br />
Hallo JSP Welt <br />
7
8
9
Prof. Dr. Peter Barth (HS-RheinMain)
</body>
</html>
Web-basierte Anwendungen
6. Juli 2015
143 / 295
JavaEE Web Tier
Java Server Pages
Direktive, Beispiel
Direktive innerhalb <%@ <directive> [<attr>="<value>"] %>
• Nachrichten an den Servlet-Container
1
• Verschiedene Direktiven <directive>
2
3
• Wichtigste Direktive: page
4
5
• Je nach Direktive verschiedene
6
Attribut (<attr>) / Wert <value> Paare 7
8
Attribut/Wert Paare von Direktive page,
Beispiel UTF-8 einstellen
9
10
11
• language: zur Zeit nur java
12
• contentType: HTTP-Header, darin
charset-Attribut für Browser
14
13
15
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
%><html>
</head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title> Insert title here </title>
<head>
<body>
<h3> Default Template </h3>
Dann klappt es auch mit XäöüßÄÖÜX im Markup
<%= new String("und mit XäöüßÄÖÜX im Code.") %>
</body>
</html>
Alles UTF-8, alles gut
• pageEncoding: Zeichensatz des
Quelltexts der Seite
• Redundant im HTML-Quelltext UTF-8
Information an Browser
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
144 / 295
JavaEE Web Tier
Java Server Pages
Weitere Attribute der Direktive page
1
import
2
• Importieren von Paketen
3
• Wie import in Java
5
4
6
7
errorPage
8
• Bei Fehler auf angegebene Seite
9
10
weiterleiten
11
12
isErrorPage
13
<%@ page import="java.util.*"
errorPage="fehler.jsp"
%><html>
<body>
<h3> Direktive </h3>
Es ist <%= new Date() %>.
<%
if (request.getParameter("bumm") != null) {
throw new RuntimeException("bumm");
}
%>
<br /><%= request.getQueryString() %>
</body></html>
• Diese Seite ist eine Fehlerseite
• Neue vordefinierte Variable exception
fehler.jsp
noch mehr Attribute
1
• session, default true
2
3
• buffer, default 8k
• autoFlush, default true . . .
Prof. Dr. Peter Barth (HS-RheinMain)
4
5
6
<%@ page isErrorPage="true"
%><html>
<body>
Uuuuups: <%= exception.getMessage() %>
<br /><%= request.getQueryString() %>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
145 / 295
JavaEE Web Tier
Java Server Pages
Direktive, include
header.jsp
Direktive include
1
• Inkludieren von anderen JSP-Frag-
menten bei Übersetzung zu Servlet
2
3
4
5
• Deklarierte Methoden und Variablen
können überall verwendet werden
• Effizient, nur ein Servlet wird erzeugt
6
7
8
9
<head>
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
%></head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title> Allgemeiner Titel </title>
<head>
• Automatisches Neukompilieren klappt
eventuell nicht immer
1
2
• Quell-Code Abhängigkeiten müssen
erkannt werden
3
4
5
6
7
8
<html>
<%@ include file="header.jsp" %>
<body>
<h3> Hello </h3>
world.
<%@ include file="footer.jsp" %>
</body>
</html>
footer.jsp
<p> <center> <strong>
2
&copy; <%= new java.util.Date() %>
3 </strong> <center> </p>
1
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
146 / 295
JavaEE Web Tier
Java Server Pages
Tag, <jsp:include>
header.jsp
Tag <jsp:include>
1
• Tag mit besonderer Bedeutung bei der
Verarbeitung auf dem Server
2
3
4
5
• Inkludieren von anderen JSP-Frag-
menten Bei Ausführung, Übersetzung
in unabhängige Servlets
6
7
8
9
<head>
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
%></head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title> Allgemeiner Titel </title>
<head>
• Deklarierte Methoden und Variablen
nur in aktuellem Fragment
1
2
• Bei Aufruf werden Servlets aller
3
inkludierten Fragmente angefragt
• Automatisches Neukompilieren klappt
4
5
6
7
8
<html>
<jsp:include page="header.jsp" />
<body>
<h3> Hello </h3>
world.
<jsp:include page="footer.jsp" />
</body>
</html>
footer.jsp
<p> <center> <strong>
2
&copy; <%= new java.util.Date() %>
3 </strong> <center> </p>
1
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
147 / 295
JavaEE Web Tier
Java Server Pages
JSP-Tags, Inkludieren und Weiterleiten
jspforwardedto.jsp
Form <jsp:action ...>
1
• Verschiedene action, zum Beispiel
include
2
3
4
5
• Ein Tag-Bibliothek, unterschiedliches
6
7
Verhalten auf dem Server
8
9
<jsp:forward page="...">
10
• Weiterleiten der Anfrage
11
12
<%@ page import="java.util.*" %>
<html><body>
Die Seite auf die weitergeleitet wurde. <br />
<%
Map<String, String[]> params =
request.getParameterMap();
for (String key : params.keySet()) {
out.print(key + " = " + params.get(key)[0]);
out.println("<br />");
}
%>
</body></html>
Parameter innerhalb include/forward
• <jsp:param name="name"
value="wert">
1
2
3
• Setzen von Parametern für inkludierte
5
oder neue Seite
6
7
Beispiel ?weiter=jawohl
8
• Achtung: Einfache Anführungszeichen
in HTML falls doppelte in Java
verwendet werden
Prof. Dr. Peter Barth (HS-RheinMain)
4
9
10
<html><body>
Die aufgerufene Seite. <br />
<% if (request.getParameter("weiter") != null) { %>
<jsp:forward page="jspforwardedto.jsp">
<jsp:param name="neu" value="wert" />
<jsp:param name=’wl’
value=’<%= request.getParameter("weiter") %>’ />
</jsp:forward>
<% } %>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
148 / 295
JavaEE Web Tier
Java Server Pages
Vordefinierte Variablen für Scopes
ServletContext application
ServletConfig config
HttpSession session
request
response
Attr.
Anfrage
Sessionattribut
Session
Servlet (aus JSP)
forward
Servlet
(JSP)
Instanzvariable*
Attribut
ServletContext
Anwendung
Lebensdauer Anwendung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
149 / 295
JavaEE Web Tier
Java Server Pages
Aktuelle Seite, pageContext
1
Aktuelle Seite
2
3
• Variable pageContext
4
• Einstieg um auf alle anderen Scopes
zuzugreifen
5
6
7
8
• Selten gebraucht
9
10
Beispiel
11
12
• Alle Attribute in allen Scopes
13
• Zugriff per Methode auf vordefinierte
Variablen
14
15
16
17
1
2
3
4
5
6
7
8
9
10
<html><body>
<h3> Alle Attribute </h3>
<% int scopes[] = { PageContext.PAGE_SCOPE,
PageContext.REQUEST_SCOPE,
PageContext.SESSION_SCOPE,
PageContext.APPLICATION_SCOPE };
for (int scope : scopes) {
java.util.Enumeration<String> e =
pageContext.getAttributeNamesInScope(scope);
while (e.hasMoreElements()) {
String name = e.nextElement();
out.print("scope "+scope+" "+name+" = ");
out.print(pageContext.getAttribute(name)+"<br />")
}
}
%>
</body></html>
<html><body>
<h3> PageContext </h3>
<%= "request "+(request == pageContext.getRequest()) %><br />
<%= "response "+(response == pageContext.getResponse()) %><br />
<%= "page "+(this == pageContext.getPage()) %><br />
<%= "page "+(page == pageContext.getPage()) %><br />
<%= "session "+(session == pageContext.getSession()) %><br />
<%= "config "+(config == pageContext.getServletConfig()) %><br />
<%= "application "+(application == pageContext.getServletContext()) %><br />
</body></html>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
150 / 295
JavaEE Web Tier
Java Server Pages
Servlets und Java Server Pages
Typische Aufgabenteilung
• JSP für die Präsentation, Darstellung
login.jsp
POST
der Daten und Abfragen
• Servlet als Controller
rauf.jsp
Controller Servlet
• Meist ein zentrales Servlet, das alle
GET
Anfragen erhält, FrontController
runter.jsp
• Aufgaben
• Ablaufkontrolle, Seitenfolgesteuerung
• Pflegen von Daten, Zustand
aktualisieren, persistente Änderungen
Beispiel
forward
• Wenn noch nicht eingeloggt, einloggen
• > 20 rauf, sonst runter um 1, 3, 10
• Drei JSP-Seiten plus ein Controller
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
Controller
Servlet
6. Juli 2015
151 / 295
JavaEE Web Tier
Java Server Pages
Controller Servlet
Controller.java
Seitenfolgesteuerung
1
2
3
• Wenn nicht
4
eingeloggt,
5
login.jsp
7
6
8
• Wenn Wert >
20,
runter.jsp
• Sonst,
rauf.jsp
Umsetzung
9
10
11
12
13
14
15
16
17
18
• Update
Session
Zustand
19
20
21
22
23
• forward
• doPost gleich
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
if (request.getParameter("login") != null)
session.setAttribute("login", request.getParameter("login"));
if (session.getAttribute("login") == null) {
RequestDispatcher rd = request.getRequestDispatcher("/login.jsp");
rd.forward(request, response);
return;
}
if (session.getAttribute("zaehler") == null)
session.setAttribute("zaehler", 0);
if (request.getParameter("diff") != null) {
int diff = Integer.parseInt(request.getParameter("diff"));
int z = ((Integer) session.getAttribute("zaehler")) + diff;
session.setAttribute("zaehler", z);
}
int zaehler = (Integer) session.getAttribute("zaehler");
if (zaehler > 20) {
RequestDispatcher rd = request.getRequestDispatcher("/runter.jsp");
rd.forward(request, response);
} else {
RequestDispatcher rd = request.getRequestDispatcher("/rauf.jsp");
rd.forward(request, response);
}
}
Web-basierte Anwendungen
6. Juli 2015
152 / 295
JavaEE Web Tier
Java Server Pages
JSP Seiten
login.jsp
Anfragen auf Servlet leiten
1
• Alle Verweise auf Servlet
2
• Konfiguration von ctrl auf Servlet
4
Controller
3
notwendig
5
6
rauf.jsp
• Direkter Zugriff auf JSP (leider) noch
möglich, vermeiden
1
2
Wenig Scriptlet-Code – Gut
3
• Nichts in login.jsp
4
• Ausschließlich Ausgaben in rauf.jsp
und runter.jsp
6
5
7
8
• Zugriff auf Attribute, Manipulation der
Attribute nur durch Servlet
<html><body>
Hallo <%= session.getAttribute("login") %> <br />
Der Wert ist <%= session.getAttribute("zaehler") %> <
Es geht
<a href="ctrl?diff=1" >eins</a>,
<a href="ctrl?diff=3" >drei</a>, oder
<a href="ctrl?diff=10" >zehn</a> rauf.
</body></html>
runter.jsp
1
2
3
4
5
6
7
Prof. Dr. Peter Barth (HS-RheinMain)
<html><body>
<form action="ctrl" method="post">
User: <input type="text" name="login" />
<input type="submit" name="ok" value="Login" />
</form>
</body></html>
<html><body>
Hallo <%= session.getAttribute("login") %> <br />
Der Wert ist <%= session.getAttribute("zaehler") %> <
Es geht
<a href="ctrl?diff=-1" >eins</a>,
<a href="ctrl?diff=-3" >drei</a>, oder
<a href="ctrl?diff=-10" >zehn</a> runter.
</body></html>
8
Web-basierte Anwendungen
6. Juli 2015
153 / 295
JavaEE Web Tier
JavaBeans
JavaBeans
Komponentenmodell für Java
• Ursprünglich für GUI-Komponenten entworfen
• Heute eine beliebige Java-Klasse, die Konventionen gehorcht
• Einsatz als Abstraktionsschicht für Logik und Datenzugriff
JavaBean-Konventionen
• Parameterloser Konstruktor, einfach erzeugbar
• Zugriff auf Eigenschaften (Properties) mit Getter und Setter Methoden
• Eigenschaft nach Java-Namenskonvention, beginnt mit Kleinbuchstaben
• Zugriff/Änderung durch Voranstellen von get/set vor Eigenschaftsname und erster
Buchstabe der Eigenschaft groß geschrieben
• Instanzen sind serialisierbar, Persistenz
• Implementieren die Schnittstelle java.io.Serializable
• Stark empfohlen: Einsatz in einem Paket
Gewähltes Komponentenmodell für JSP
• Unterstützung in JSP für JavaBeans
• Einfache Verwendung, einfache Syntax, Tool-Unterstützung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
154 / 295
JavaEE Web Tier
JavaBeans
Beispiel-Anwendung mit Bean
Typfehler
Bean mit Daten einer Person
• Getter und Setter
• Überprüfung ob Daten ok
Formularseite mit Eingabefelder für Daten
• Auf der Seite bleiben bis alles ok
• Fehlerseite bei Typfehler
ungültig
Abschließende Ausgabeseite
alles ok
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
155 / 295
JavaEE Web Tier
JavaBeans
Beispiel Bean – NameBean
Bean
1
2
3
• Parameterloser 4
Konstruktor,
sinnvolle
Default-Werte
5
6
7
8
9
• Getter und
Setter
• Setter mit
Logik möglich,
vermeiden
• Zusätzliche
Convenience
Getter
10
11
12
13
14
15
16
17
18
19
20
21
22
• Logik
23
public class NameBean implements Serializable {
String vorname; String nachname;
String mittelname; int alter;
public NameBean() { // parameterloser Konstruktor
vorname = "Max"; mittelname="M.";
nachname = "Mustermann"; alter = 0;
}
public String getVorname() { return vorname; }
public void setVorname(String vorname) { this.vorname = vorname; }
public String getMittelname() { return mittelname; }
public void setMittelname(String mittelname) { this.mittelname = mittelname; }
public String getNachname() { return nachname; }
public void setNachname(String nachname) { this.nachname = nachname; }
public int getAlter() { return alter; }
public void setAlter(int alter) { this.alter = alter<0 ? 0 : alter; }
public String getName() { // Convenience, lesend
return vorname+" "+mittelname+" "+nachname;
}
public boolean gueltig() { // Logik
return alter > 0 &&
(vorname+mittelname+nachname).length() >= 4;
}
}
Erzeugte class-Datei unter WEB-INF/classes mit Pakethierachie
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
156 / 295
JavaEE Web Tier
JavaBeans
JavaBeans und JSP
<jsp:useBean>: Bean-Instanz in JSP-Seite verfügbar
1
2
3
• id: Der Variablenname der Bean
4
• class: Die Klasse mit Paketangabe aus der die
Instanz wenn notwendig erzeugt wird
<jsp:useBean
id="nb"
class="webanw.NameBean"
scope="session" />
5
6
7
8
• type: Alternativ zu class, gibt Instanz anderen Typ
(Ober-/Unter-klasse), ohne class wird die Instanz
nicht erzeugt sondern muss schon existieren
• scope: Lebensdauer der Bean (page, request,
session, application)
9
10
11
<jsp:getProperty
name="nb"
property="vorname" />
12
13
14
15
<%=
nb.getVorname().toString()
%>
16
<jsp:getProperty>: Property-Zugriff, als String
17
18
• name: Variablenname, wie unter id vergeben
19
20
• property: Name der Eigenschaft
<jsp:setProperty>: Property setzen
• name, property (wie getProperty)
22
23
25
27
Web-basierte Anwendungen
<jsp:setProperty
name="nb"
property="vorname"
value="Max" />
24
26
• value: Zu setzender Wert
Prof. Dr. Peter Barth (HS-RheinMain)
21
<%
nb.setVorname("Max");
%>
6. Juli 2015
157 / 295
JavaEE Web Tier
JavaBeans
Beispiel Formular, Header, Fehler
1
• Zeile 3: NameBean unter
nb verfügbar machen
• Komische
2
3
4
5
6
Formatierung? Keine 7
8
Leerzeichen an
9
Browser schicken
10
• Fehlerbehandlung bei
Typkonversion
11
12
13
• Manuell Ausnahme
fangen und
aussagekräftigere
(anwendungsbezogen)
werfen
• Weiterleiten auf
deklarierte
Fehlerseite
automatisch
Prof. Dr. Peter Barth (HS-RheinMain)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" errorPage="namebeanerror.jsp"
%><jsp:useBean id="nb" class="webanw.NameBean" scope="session"/><%
if (request.getParameter("ok") != null) {
if (request.getParameter("alter") != null) {
try {
Integer.valueOf(request.getParameter("alter"));
} catch (NumberFormatException e) {
throw new RuntimeException("alter sollte eine Zahl sein " +
" und nicht " + request.getParameter("alter"));
}
}
%>
namebeanerror.jsp
1
2
3
4
5
6
7
8
9
10
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isErrorPage="true" %>
<html><head><title>Fehler-Seite</title></head>
<body><h3> Fehler </h3>
Hey, pass besser auf: <br />
<%= exception.getMessage() %> <br />
<%--for (StackTraceElement ste : exception.getStackTrace()) {
out.println(ste); }
--%> <br />
</body></html>
Web-basierte Anwendungen
6. Juli 2015
158 / 295
JavaEE Web Tier
JavaBeans
Beispiel Formular, Verarbeitung
setProperty,
Übernahme aus Formular
• Mit value hantieren ist unangenehm
• Übernahme mit param, optional wenn Parameter-/Eigenschafts-name gleich
• Automatische Typkonversion von String nach Grundtypen/Wrapper
1
2
3
4
5
6
7
8
9
10
11
12
<jsp:setProperty name="nb" property="vorname" value=’<%= request.getParameter("vorname") %>’/>
<jsp:setProperty name="nb" property="mittelname" param="mittelname"/>
<jsp:setProperty name="nb" property="nachname"/>
<jsp:setProperty name="nb" property="alter" param="alter"/>
<% } if (nb.gueltig()) {
%><jsp:forward page="namebeanok.jsp" /><% } %><html>
<head><title>NameBean Eingabe</title></head><body>
Hallo <jsp:getProperty name="nb" property="name"/>,
<form method="post">
<table><tr><td>Vorname</td><td>
<input type="text" name="vorname" value="<jsp:getProperty name="nb" property="vorname"/>">
</td></tr>
• Ausgabe ab Ende Zeile 6, vorher Verarbeitung Eingabe/neuer Zustand
• Zeile 11: Nach JSP-Verarbeitung ist das wieder ein String
• Falls Parametername gleich Eigenschaftsname:
<jsp:setProperty name="nb"property="*"/>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
159 / 295
JavaEE Web Tier
JavaBeans
Beispiel, Ausgabe
namebeanok.jsp
Ausgabe
• Zeile 3: Zur Verfügung
stellen von Namebean
unter Name nb
1
2
3
4
5
6
• Es wird Whitespace
an Browser
ausgegeben
7
8
9
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<jsp:useBean id="nb" class="webanw.NameBean" scope="session"/>
<html><head><title>Name Bean OK</title></head><body>
Hallo <jsp:getProperty name="nb" property="name"/>. <br />
Sie sind
<jsp:getProperty name="nb" property="alter"/>
Jahre alt. <br />
</body></html>
• Nur ausgeben von
Daten
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
160 / 295
JavaEE Web Tier
Expression Language
Expression Language – Motivation
1
Problem mit Scriptlets
2
• Selbst einfache Ausdrücke nur mit
Scriptlets möglich, zum Beispiel
Addition
3
4
5
6
• Fehlende vordefinierte Variablen, zum
<h3> Ohne Expression Language </h3>
<%= 1 + 2 + 3 %> <br />
<%= ((Cookie) request.getCookies()[0]).getValue() %>
<br />
<jsp:getProperty name="nb" property="vorname" />
<jsp:getProperty name="nb" property="nachname" />
<h3> Mit Expression Language </h3>
${1 + 2 + 3}<br />
3 ${cookie.JSESSIONID.value}<br />
4 ${nb.vorname} ${nb.nachname}
1
Beispiel Cookies
2
• Komplexe Formulierung, zum Beispiel
Zugriff auf Properties
Ziel
• Vermeiden von Scriptlets
• Wichtige Variablen vordefiniert
• Einfache Ausdrücke
Lösung: Expression Language
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
161 / 295
JavaEE Web Tier
Expression Language
Syntax und Operationen
Spezielle Ausdrücke ${<expr>}
• Werden an aktueller Stelle im Text ersetzt1
• Syntax: Ausdruck <expr> zwischen ${ und }
• Unterstützung für Literale: Zahlen, Boolesche Werte, String, null
Operatoren
Arithmetische Operationen
: +, -, *, /, div, %, mod
Vergleichsoperationen
: ==,eq, !=,ne, <,lt, >,gt, <=,le, >=,ge
Test ob leer (wahr bei null und lee- : empty
rem String, Array, Map, Collection)
Funktionsaufruf (taglib)
: <func>(<args>)
Konditionaler Ausdruck
: <expr> ? <wert-true> : <wert-false>
Gruppieren von Ausdrücken
: ()
Zugriff Feld, Liste
: [<index>]
Zugriff Map, Bean-Eigenschaft
: .<key/propertyname>
1
Wir sehen später, dass man besser <c:cout value="${<expr>}"/> verwenden sollte
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
162 / 295
JavaEE Web Tier
Expression Language
Operatoren, Beispiele
<jsp:useBean id="t" class="webanw.TestBean"/>
Beans
• Stehen nach
zur
Verfügung
useBean
Feldzugriff
• Bean gibt
String[]
mit
getStringArray()
zurück
Maps
• Dot-Notation
• cookie ist Map
von String
(Name) nach
• Cookie hat Methode getValue()
• cookie ist eine Map, cookies ein Array
Cookie
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
163 / 295
JavaEE Web Tier
Expression Language
Vordefinierte Variablen, Maps
Maps, Kollektionen
Variablen des jeweiligen Scopes
: pageScope, requestScope,
sessionScope, applicationScope
Anfrage-Parameter, Wert ein String
: param
Anfrage-Parameter, Wert String-Array (bei : paramValues
mehreren je Schlüssel sinnvoll)
Anfrage-Header (HTTP)
: header, headerValues
Map von Cookies
: cookies
Initialisierungsparameter
Anwendung, : initParam
Wert ein String
Seitenkontext
• Zum Zugriff auf alle anderen Werte
Zugriff auf Maps
pageContext
• Dot-Notation
• ${cookie.JSESSIONID.value}
• Alternativ mit [], muss bei
• ${cookie[’JSESSIONID’][’value’]}
Sonderzeichen (zum Beispiel ’-’ im
Propertynamen) verwendet werden
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
164 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
Standard Tag Library (JSTL)
<jsp:useBean
Standard JSP Tags
• Vordefinierte Tags, spezielle Bedeutung
id="nb"
class=
• Fester Namensraum (jsp) mit Tags (useBean)
"webanw.NameBean"
/>
Custom Tags, Custom Tag Library
• Tags selbst entwickeln, Scriplets vermeiden
• (Anwendungs-)Java-Code statt Tags ausführen
Java Standard Tag Library (JSTL)
• Häufig gebrauchte Tags, vereinheitlicht
• JSTL 1.2.1 Spez. für JSP 2.2/Servlet 3.0 (auch
Tomcat 8)
<c:forEach var="v"
• Core (c), XML-Processing (x), I18N-Formatierung
(fmt), Datenbanken (sql), Funktionen (fn)
items="${header}" >
<c:out value=
"${v.key}=${v.value}"
• Wichtigster Teil Core
• Ausgabe, Verzweigung, Schleifen, URL-Manipulation,
/>
</c:forEach>
Importieren, . . .
• Implementierung nicht Teil des Servlet-Containers
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
165 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Einbinden
JSTL Implementierung
• JAR-Dateien,
javax.servlet.jsp.*
• jstl-1.2.1.jar,
jstl-api-1.2.1.jar
Zielordner Web-Anwendung
• WEB-INF/lib
• Auch in Eclipse an diese Stelle
(unterhalb WebContent)
• Wird von selbst sichtbar in Web
App Libraries
Verwenden mit taglib-Direktive
• uri: Identifikation Tag-Library
• prefix: Namensraum für Tags
• Danach mit Präfix (c) verwenden
Prof. Dr. Peter Barth (HS-RheinMain)
<%@ taglib
uri="http://java.sun.com/jsp/jstl/core"
prefix="c"
%>
<c:out value="Hallo"/>
Web-basierte Anwendungen
6. Juli 2015
166 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Core, <c:out>
jsp
Tag <c:out>
1
• Ausgabe Wert an aktueller Stelle
2
3
4
Attribute
5
• value: Auszugebender Wert, muss
6
• escapeXML: Sonderzeichen
konvertieren, Vorgabe "true"
8
7
9
10
• default: Falls value==null, kann
auch Body des Tags sein
Beispiel
<%@ taglib prefix = "c"
uri="http://java.sun.com/jsp/jstl/core"
%><html><body>
<c:out value="Hallo Welt" /><br />
<c:out value="<c:out>" /><br />
<c:out value="<c:out>" escapeXml="false" /><br />
<c:out value="${cookie.JSESSIONID.value}" /><br />
<c:out value="${request.bla}">
bla-Parameter nicht definiert.
</c:out></body></html>
html
1
2
3
• Ausgabe von Text mit Tags
4
• Expression Language in Tags
6
5
<html><body>
Hallo Welt<br />
&lt;c:out&gt;<br />
<c:out><br />
<br />
bla-Parameter nicht definiert.</body></html>
verwenden
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
167 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Core, <c:set>
1
Tag <c:set>
2
3
• Setzen von Attributen
4
von Maps, Beans
5
6
Attribute
7
8
• var: Variablenname
• value: Wert
• scope:
9
10
11
12
13
Gültigkeitsbereich
(page, request,
14
session,
application)
17
15
16
<c:set var="i" value="1" />
<c:out value="i=${i}" /><br />
<c:set var="text" value="Hallo Welt" scope="page" />
<c:out value="text=${text}" /><br />
<!-- t ist eine Bean -->
<c:set target="${t}" property="name" value="Hallo Welt" />
<c:out value="text=${t.name}" /><br />
<!-- t.getMapString() liefert Map<String, Object> -->
<c:set var="a" value="${t.mapString}" scope="page" />
<c:set target="${a}" property="foo" value ="bar" />
<c:out value="a.foo=${a.foo}" /><br />
<!-- bei Sonderzeichen wären [] notwendig -->
<c:out value="a[’foo’]=${a[’foo’]}" /><br />
<c:out value="a[3]=${a[’3’]}" /><br />
<c:set target="${a}" property="3" value="42" />
<c:out value="a[3]=${a[’3’]}" />
<c:remove var="text" scope="page"/>
• target: Zielobjekt, Map
oder Bean
• property: Schlüssel
oder Eigenschaft
Tag <c:remove> möglich
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
168 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Core, <c:forEach>
Tag <c:forEach>
• (Erweiterte) for-Schleife
• Verschachteln erlaubt
Attribute
• items: Iterierbares Objekt
(Collection, Iterator,
Enumeration, Map, Object[],
int[], . . .
1
2
3
4
5
6
7
8
9
10
11
<c:forEach var="var" items="${header}">
<c:out value="${var.key} = ${var.value}" /><br />
</c:forEach>
<c:forEach var="var" items="${headerValues}">
<c:forEach var="val" items="${var.value}">
<c:out value="${var.key} = ${val}" /><br />
</c:forEach>
</c:forEach>
<c:forEach var="i" begin="1" end="4">
<c:out value="${i}*${i} = ${i*i}" /><br />
</c:forEach>
• var: Name der Laufvariable, der
Elemente aus items
zugewiesen werden
• Keine Expression Language
erlaubt
• begin, end, step: Alternative,
klassische for-Schleife
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
169 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Core, <c:forEach>, Statusvariable
Zusatzinformationen
• Optionales Attribut varStatus
• Typ LoopTagStatus
Eigenschaften der Statusvariablen
• begin, end, step: Wie
angegeben oder null
• first, last: Boolesch, am
1
2
3
4
5
6
7
8
9
10
11
12
13
<c:forEach var="i" begin="1" end="10" step="3"
varStatus="stat">
<c:out value="${i}=${stat.current}" />
<c:out value="Schleife von ${stat.begin} bis" />
<c:out value=" ${stat.end} step ${stat.step}" />
<c:out value=" first=${stat.first} last=${stat.last}" />
<c:out value=" count=${stat.count} index=${stat.index}" />
<br />
</c:forEach>
<c:forEach var="prim" items="${t.intArray}"
varStatus="stat">
<c:out value="${stat.index}: ${prim}" />&nbsp;
</c:forEach>
ersten, letzten?
• count: Das wievielte
• current: Aktuelles Element
• index: Aktueller Index
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
170 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Core, <c:if>
1
Tag <c:if>
2
3
• Verzweigung
4
5
• Kein else
6
7
Attribute
8
• test: Ausdruck, der zu true oder
false evaluiert
9
10
11
12
• var: Variablenname der neuen
13
<c:if test="${t.name eq ’TestBean’}">
Name Property der TestBean ist noch TestBean
</c:if><br />
<c:if test="${t.name ne ’TestBean’}">
Name Property der TestBean ist
<c:out value="${t.name}" />
</c:if><br />
<c:set target="${t}" property="name"
value="Hello World" />
<c:if test="${t.name ne ’Hallo Welt’}">
Name Property der TestBean ist
<c:out value="${t.name}" />
</c:if><br />
Booleschen Variable mit Ergebnis des
Tests, optional
• scope: Scope für die neue Variable
aus var
Alternative <c:choose>, wie switch
• <c:when>: test-Attribut,
wie case mit break
• <c:otherwise>: wie default
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
171 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
Weitere Standard Tags – Auszug
• <c:forToken var="v" items="a,b,c" delims=","> ...:
Einfache for-Schleife, wie for v in [’a’, ’b’, ’c’]
• <c:catch var="exception"/>:
Ausnahme fangen
• <c:import url="http://www.mi.hs-rm.de/portal" />:
Inkludieren von Inhalten, wie <jsp:include />, aber auch externe Ressourcen
möglich
• <c:redirect url="http://www.mi.hs-rm.de" />:
Redirect über Browser
• <c:url value="http://www.mi.hs-rm.de" />:
Ausgabe einer URL, passende Konvertierung der Zeichen
• <c:param name="arg1" value="val1" />:
Innerhalb von <c:import>, <c:redirect>, <c:url>; zum Anhängen von
GET-Parametern
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
172 / 295
JavaEE Web Tier
Standard Tag Library (JSTL)
JSTL Funktionen für Expression Language
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ taglib prefix = "c"
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix = "fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
<c:if test="${fn:contains(header[’User-Agent’], ’MSIE’)}">
Ieee, IE.
</c:if>
<c:if test="${fn:containsIgnoreCase(header[’User-Agent’], ’firefox’)}">
Aaaa, Firefox.
</c:if><br />
<c:if test="${fn:endsWith(header.referer, ’jstl/’)}">
jstl/ am Ende beim referer
<c:out value="${header.referer}" />
</c:if>
Funktionen (functions)
Convenience-Funktionen
• Verwendung durch separate
taglib-Deklaration, URL
• Üblicher Präfix, fn
• Einsatz innerhalb von EL
• Meist zusammen mit Core verwendet,
meist in Attributen
Prof. Dr. Peter Barth (HS-RheinMain)
• +escapeXML, indexOf, join,
length, replace, split,
startsWith, substring,
substringAfter, substringBefore,
toLowercase, toUpperCase, trim
• weitgehend selbsterklärend, Doku
Web-basierte Anwendungen
6. Juli 2015
173 / 295
JavaEE Web Tier
i18n, l10n
Internationalisierung, Lokalisierung
internationalization (i18n), localization (l10n)
• Abkürzung, 18/10 Buchstaben ersetzt
Internationalisierung
• Anwendung so entwickeln, dass sie international eingesetzt werden kann
• Vorbereiten/Ermöglichen der Lokalisierung, zum Beispiel Verwenden von
Schlüssel/Wert-Paaren für die Verwaltung der Texte
Lokalisierung
• Anwendung an lokale Gegebenheiten anpassen
• Übersetzung der Texte, zum Beispiel Menüs, Label, . . .
• Formatierungskonventionen
• Zahldarstellung: 100.000,00 oder 100,000.00
• Maßeinheiten: Euro oder Dollar, Meter oder Inch
• Zeit: 13:00 oder 1:00 pm
• Datumsangaben: 6/1/2014 oder 1.6.2014, Januar oder Jänner
• Passender Zeichensatz
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
174 / 295
JavaEE Web Tier
i18n, l10n
JavaEE Web Tier und i18n/l10n
Auswahl der Sprache und Formatierung
• Locale
• Repräsentation welche Sprache und welche Region
Nutzen der passenden Sprache
• Ressource-Dateien
• Schlüssel/Wert-Paare für Übersetzungen
Nutzen der passenden Texte und Formate
• Tag <fmt:...> der JSTL
• Für Formatierungen
• Für Textauswahl in richtiger Sprache
• Alternative
• Aufspaltung in verschiedene Seiten je Sprache/Region
• Sehr aufwendig, meist Ergebnis von sinnlosem Copy & Paste
• Vermeiden
Dokumentation lesen, große Teile schon in JavaSE
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
175 / 295
JavaEE Web Tier
i18n, l10n
Auswahl Lokalisierung mit Locale
Web-Browser, Accept-Language
Locale:
• HTTP-Header
1
• Komma-separierte Liste aus
3
• Sprachkürzel (RFC 3066), de
für Deutsch, en für Englisch
• Sprach-/Länderkürzel (RFC
1766), de-DE für Deutschland,
de-CH für Schweiz
• Optional Gewichtung mit ;
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Prof. Dr. Peter Barth (HS-RheinMain)
Java-Klasse für Sprache/Länder
public class LocaleServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Locale locale = request.getLocale();
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.print("Sprache: ");
out.print(locale.getDisplayLanguage() + "<br />");
out.println("Spracheinstellungen Browser: <ul>");
Enumeration<Locale> e = request.getLocales();
while (e.hasMoreElements()) {
locale = e.nextElement();
out.println("<li>"+locale+"</li>");
}
out.println("</ul>\nAccept-Language:");
out.println(request.getHeader("Accept-Language"));
out.println("</body></html>");
}
}
Web-basierte Anwendungen
6. Juli 2015
176 / 295
JavaEE Web Tier
i18n, l10n
Ressourcedateien
Ressourcedateien auf dem Server
Property-Dateien für texte
• Festes Namensschema
• Zwei Schlüssel, hello und ciao
<name>_<sprache>_<land>_<variante>.properties
• Alles außer name optional
Unterhalb von Paket webanw
• Inhalt sind ASCII Schlüssel/Wert-
mitparam.properties
texte_de_CH.properties
texte_de.properties
texte_en.properties
texte.properties
Paare, Unicode Escape erlaubt
(native2ascii)
• Ablage unterhalb WEB-INF/classes
• In Paketstruktur, JavaSE
texte.properties, texte_en.properties
Klasse java.util.ResourceBundle
hello=Hello
2 ciao=Bye
1
• Repräsentation Dateiinhalt in Java
texte_de.properties
• Siehe Dokumentation für Methoden
• Nicht mit JSP, geht da einfacher
hello=Hallo
2 ciao=Tsch\u00fc\u00df
1
Suchreihenfolge
• Alles passt genau
• Dann alles außer Variante, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
texte_de_CH.properties
hello=Gr\u00fcezi
2 ciao=Auf Wiederlurge
1
Web-basierte Anwendungen
6. Juli 2015
177 / 295
JavaEE Web Tier
i18n, l10n
Verwendung von Ressourcedateien mit <fmt:...>
1
Verwenden mit <fmt:...>
2
3
• Einbinden mit taglib
4
• Nutzen mit message
5
Ressourcebundle wählen
7
6
8
• Default ist Browser-Locale
9
10
• Manuell mit <fmt:setBundle> und
Attribut basename, vollqualifizierter
Name
11
12
13
14
15
• Gültig für die ganze Seite
16
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
<html><body>
<fmt:bundle basename="webanw.texte">
<fmt:message key="hello" /><br />
<fmt:message key="ciao" /><br />
</fmt:bundle>
<fmt:setBundle basename="webanw.texte" />
<fmt:message key="hello" /> +
<fmt:message key="ciao" /><br />
<fmt:setLocale value="de_CH" />
<fmt:setBundle basename="webanw.texte" />
<fmt:message key="hello" var="start"/>
<fmt:message key="ciao" var="stop" />
${start} + ${stop} <br />
</body></html>
• Gültigkeit für Bereich mit
<fmt:bundle></..>
Text-Ausgaben
• <fmt:message>
• Attribute key und optional bundle
• Attribut var, in Variable statt Seite
Prof. Dr. Peter Barth (HS-RheinMain)
Im Eclipse-Editor werden
Ressourcedateien automatisch konvertiert
(keine Unicode-Escapes notwendig)
Web-basierte Anwendungen
6. Juli 2015
178 / 295
JavaEE Web Tier
i18n, l10n
Ressourcen und Formatierungsparameter
hello=Hallo
ciao=Ciao
3 pershello=Hi {0}, wie geht’s?
1
Problem: fixe Texte
2
• Schlüssel/Wert Paare geht nur für
feste Textfragmente
<c:set var="name" value="Susi" />
<fmt:message key="pershello">
3
<fmt:param value="${name}" />
4 </fmt:message>
1
• Parametrisierte Texte, wie zum
2
Beispiel ein personalisierter Gruß, ist
nicht darstellbar
Lösung: Parameter
• Parameter in Texten in
Ressourcendateien
• {n}, Zahlen n ab 0 für zu ersetzende
Stellen
• Parameterübergabe bei Verwenden
von <fmt:message>
• <fmt:param value="<wert>"/>
• Reihenfolge bestimmt Verwendung
als 0tes, 1tes, 2tes, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
179 / 295
JavaEE Web Tier
i18n, l10n
Formatierung für Länder, Regionen und Zeitzonen
Formatierungen spezifisch
für eine Region
• Zeit- und
Datumsangaben,
Aussehen und Werte
Beispiel
• 2,000.00 $, 4/1/2014 versus 1.435,00
e, 1.4.2014
• Greenwich Mean Time (GMT) versus Universal Time
Coordinated (UTC)
• Wiesbaden: GMT+1
• Zahldarstellung
• Währungsdarstellung
und Geldbeträge
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
180 / 295
JavaEE Web Tier
i18n, l10n
Datums- und Zeitangaben
1
Datum und Zeit
2
3
• Wie in Java
4
5
Tag fmt:formatDate, Attribute
6
7
• value: Ein Date-Objekt
8
• type: date oder time
9
10
• dateStyle/timeStyle:
11
12
vordefinierter Stil
13
14
• pattern: spezifisches Ausgabepattern15
• timeZone: statt aktueller Zeitzone
16
17
<c:set var="jetzt"
value="<%= new java.util.Date() %>" />
<c:forTokens var="stil"
items="default,short,medium,long" delims=",">
<c:out value="${stil}" />:
<fmt:formatDate value="${jetzt}" type="date"
dateStyle="${stil}" />&nbsp;
<fmt:formatDate value="${jetzt}" type="time"
dateStyle="${stil}" /><br />
</c:forTokens>
US-Datum:
<fmt:formatDate value="${jetzt}" type="date"
pattern="MM/dd/yyyy" /><br />
In New York ist es jetzt
<fmt:formatDate value="${jetzt}" type="date"
pattern="hh:mm:ss a"
timeZone="America/New_York" />
• java.util.TimeZone
(.getAvailableIDs())
• var: Ausgabe in eine Variable
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
181 / 295
JavaEE Web Tier
i18n, l10n
Zahlen und Geldbeträge
1
Siehe Beispiel
2
3
• value immer
US-Stil ohne
Gruppierung,
wie in
Programmiersprachen
4
5
6
7
8
9
10
11
12
• Default
13
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
<fmt:formatNumber
value="1234567.89"/><br />
value="1234567.89" maxFractionDigits="1"/><br />
value="1234567.89" maxIntegerDigits="3"/><br />
value="1234567.89" groupingUsed="false"/><br />
value="1234567.89" minFractionDigits="5"/><br />
value="1234567.89" pattern="###.#####"/><br />
value="1234567.89" pattern="###.00000"/><br />
value="1234567.89" type="number"/><br />
value="0.1234" type="percent"/><br />
value="0.1234" type="percent" minFractionDigits="2"/><br />
value="1234567.89" type="currency"/><br />
value="1234567.89" type="currency" currencyCode="USD"/><br />
value="1234567.89" type="currency" currencySymbol="$"/><br />
Einstellungen
nach Locale
• currencyCode
nach ISO-4217
• Tag
fmt:parseNumber
analog
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
182 / 295
JavaEE Web Tier
i18n, l10n
Zeichensätze
Zeichensätze, Character Encoding
• Vorgabe ist ISO-8859-1 (Western-European), latin-1
• Standard in HTTP (Das Web kommt von den Europäern ;-)
• Alternative: UTF-8, präferiert
UTF-8
• Spezieller kompakter Unicode, wenn möglich nur 8 Bit
• Im Gegensatz zu latin-1 sind nicht alle Zeichen nur 8 Bit groß, wie zum Beispiel
die deutschen Umlaute
• In Java wird Unicode verwendet, allerdings nicht unbedingt UTF-8
• Vorteil von UTF-8: (Fast) alle Zeichen darstellbar
Was verwenden? Empfehlung? – UTF-8
• An der Hochschule UTF-8, LANG=de_DE.UTF8, in der Shell echo $LANG
• Ist Vorgabe in Eclipse-Template
• Alle Browser können es, meist keine Problem
• Nie Nie Nie Nie Nie MacRoman oder cp1252
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
183 / 295
JavaEE Web Tier
Custom Tags
Custom Tags
1
Problem – Gleichartige Teile
2
3
• Darstellung von Teilen häufig
4
gleich(artig) auf verschiedenen Seiten
5
6
• Kopieren jeweiliger JSP Code-Blöcke
7
8
<center>
Copyleft &copy;
<jsp:useBean id="jetzt"
class="java.util.Date" scope="session"
/><fmt:formatDate var="year" value="${jetzt}"
pattern="yyyy"
/><c:out value="${year}" />
</center>
• Bei Änderungen alle Kopien ändern
• „Makro“-Feature notwendig
Lösungsansatz – Custom Tags
• JSP-Fragmente wiederverwenden
<center>
Copyleft &copy;
2015
</center>
• Benutzen als eigene (custom) Tags
Tag-Dateien: JSP-Fragmente plus Syntax
• Je Custom Tag separate Datei unter
WEB-INF/tags oder Unterordner
Tag-Klassen: Java-Code
• Java-Klassen, erben von CustomTag
• Bereitstellung über web.xml
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
184 / 295
JavaEE Web Tier
Custom Tags
Copyleft Tag als Tag-Datei
copyleft.tag
Tag-Datei
• Ablageort
WEB-INF/tags/copyleft.tag
1
2
3
4
5
• Direktive tag, keinen Body (viele 6
Tags haben keinen Body)
Verwendung
• Direktive taglib, Angabe von
Verzeichnis mit tagdir, immer
WEB-INF/tags verwenden
7
8
9
10
11
12
13
14
usecopyleft.jsp
• Eigenes Tag, ersetzt durch
Inhalt Tag-Datei
Eclipse-Unterstützung nicht
umfassend
Prof. Dr. Peter Barth (HS-RheinMain)
<%@ tag body-content="empty" %>
<jsp:useBean id="jetzt"
class="java.util.Date" scope="session" />
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setLocale value="de_DE" /> <!-- don’t ask -->
<center>
Copyleft &copy;
<fmt:formatDate var="year" value="${jetzt}"
pattern="yyyy"
/><c:out value="${year}" />
</center>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
2 <html><body>
3 <my:copyleft />
4 </body></html>
1
Web-basierte Anwendungen
6. Juli 2015
185 / 295
JavaEE Web Tier
Custom Tags
Tag-Dateien
Ablage
/WEB-INF/tags/....tag
• Datei mit Endung .tag, unter /WEB-INF/tags
• Unterordner für separate Tag-Bibliotheken ok
• Können in JAR-Dateien verpackt werden
Definieren mit Tag-Direktive
<%@ tag body-content="..."%>
• empty: Kein Body im Tag erlaubt
• scriptless: Keine Scriptlets erlaubt (default), aber sonst alles (Direktiven,
Tag-Libraries, JSTL, EL)
• tag-dependant: Text bleibt wie er ist (keine Direktiven, keine JSTL, kein EL)
Verwenden
<%@ taglib prefix="my"tagdir="/WEB-INF/tags"%>
• In beliebiger JSP-Datei mit Taglib-Direktive
• Beliebiger Präfix, Überschneidung mit JSTL vermeiden
• Pfad, eventuell mit Unterordner, zur Ablage
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
186 / 295
JavaEE Web Tier
Custom Tags
Attribute in Custom Tags, Beispiel
Konfigurierbares Bild
• Ein Bild über eine URL einbinden, Muss-Attribut
• Visualisierung mit Titel im Template/Tag
• Angabe des Titels als Attribut, wenn nicht
angegeben, dann ???
Verwendung
1
2
3
4
5
6
7
8
9
10
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core"
%><%@ taglib prefix="my" tagdir="/WEB-INF/tags"
%><html><body>
<c:url var="url" value="/images/rabbit.png" />
Mit Titel <br />
<my:image url="${url}" title="Ein Hase" />
Ohne Titel <br />
<my:image url="${url}" />
</body></html>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
187 / 295
JavaEE Web Tier
Custom Tags
Attribute in Custom Tags, Beispiel, Tag-Datei
Attribute
image.tag, Bilder Beispiel
• Eine Direktive je Attribut
1
• Nach der Tag-Direktive
2
3
Attribut-Direktive
4
5
<%@ attribute
6
name="..."
required="..." %>
7
8
9
• name: Der Bezeichner des Attributs
10
11
<%@ tag body-content="empty" %>
<%@ attribute name="url" required="true" %>
<%@ attribute name="title" required="false" %>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<table>
<tr><td><img alt="${title}" src="${url}">
</td></tr><tr><td align="center">
<c:out value="${not empty title ? title : ’???’}"
/></td></tr>
</table>
• required: true oder false,
bei true Fehler, falls Attribut bei
Verwendung nicht angegeben wird
Lebenszeit der Attribute
• Wird der Variablen von name
• Kann in Expression Language
zugewiesen
• PageScope, aber nur innerhalb der
verwendet werden
Tag-Datei, eigentlich TagScope
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
188 / 295
JavaEE Web Tier
Custom Tags
Nicht deklarierte Attribute verwenden
dynatts.tag
Nicht deklarierte Attribute
1
• Attribute, die nicht per Direktive
2
3
angegeben wurden
4
• Erlauben mit neuem Attribut
5
6
Tag-Direktive, dynamic-attributes
7
<%@ tag body-content="empty"
dynamic-attributes="attrs"%>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<c:forEach var="attr" items="${attrs}">
<c:out value="${attr.key}=${attr.value}" /><br />
</c:forEach>
• dynamic-attributes: Variablenname,
Map in der alle weiteren Attribute
gesammelt werden
• Explizite Attribute zusätzlich erlaubt,
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<html><body>
3 <my:dynatts hallo="Hallo" var="wert" foo="bar" />
4 </body></html>
1
2
erscheinen nicht in Map
Beispiel
• Alle Attribute mit Namen und Wert
ausgeben
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
189 / 295
JavaEE Web Tier
Custom Tags
Tag Body verarbeiten, jsp:doBody
codeformat.tag
Tag Body verarbeiten
1
• In Tag-Direktive erlauben
<%@ tag
body-content="scriptless" %>
2
• Body auslesen mit jsp:doBody
7
Beispiel
• Mini Wiki
4
5
6
8
• var: Body des Tags in eine Variable
• varReader: Alternativ als
Eingabestrom
3
9
10
11
12
13
1
2
• Darstellung von Code
3
4
5
6
7
8
9
10
11
12
Prof. Dr. Peter Barth (HS-RheinMain)
<%@ tag body-content="scriptless" %>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
<jsp:doBody var="text0" />
<c:set var="text1"
value="${fn:escapeXml(text0)}" />
<c:set var="text2"
value="${fn:replace(text1, ’[code]’, ’<pre>’)}"
<c:set var="text3"
value="${fn:replace(text2, ’[/code]’, ’</pre>’)}
${text3}
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<html><body>
<my:codeformat>
Das ist ein normaler Text mit <tags>,
die angezeigt werden. Man kann Code
[code]
for s in [’Hallo’, ’Welt’]:
print s
[/code]
mit speziellen Tags formatieren.
</my:codeformat>
</body></html>
Web-basierte Anwendungen
6. Juli 2015
190 / 295
JavaEE Web Tier
Custom Tags
Attribut-Fragmente und sichtbare Variablen
Ziel
• Mischen von Tag-Code und Seiten-Code
• Tag-Code in Seiten und Seiten-Code in Tag
Attribut-Fragmente
<%@ attribute name="..." fragment="true" ... %>
• Attribute im Tag-Datei als Fragmente deklarierbar
• Stehen in Tag-Datei unter dem Namen zur Verfügung
• Attribute in Tag-Datei verwenden
<jsp:invoke name="..." />
• Attribute in JSP-Datei deklarieren
<jsp:attribute name="..." >
...</jsp:attribute>
Sichtbare Variablen
<%@ variable name-given="..."
variable-class="..." scope="..." %>
• Variablen in Tag-Datei für JSP-Datei sichtbar machen
• Mit variable-Direktive
• name-given: Name der Variable in der JSP-Datei
• variable-class: Java-Typ der Variablen, Object meist ok
• scope: Sichtbarkeit, AT_BEGIN nach Tag-Start, AT_END nach dem Tag, NESTED im Tag
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
191 / 295
JavaEE Web Tier
Custom Tags
Fragmente & sichtbare Variablen, Beispiel Tableformat
freddytable.jsp
Ziel - Bunte Tabelle
1
2
• Man unterscheidet zwischen
3
geradzahligen Zeilen und
ungeradzahligen
4
5
6
• Der Benutzer legt die Inhalte fest, im
7
8
<%@ taglib prefix="my" tagdir="/WEB-INF/tags"
%><html><body><my:table
items="eine,schicke,bunte,Tabelle"
><jsp:attribute name="even"
>${current}</jsp:attribute><jsp:attribute name="odd"
><strong> ${current} </strong></jsp:attribute
>
</my:table></body></html>
Beispiel sollen ungeradzahlige Zeilen
fett werden
• Der Tag-Entwickler legt die Farben
fest, im Beispiel Freddy Kröger
Design*
<html><body>
<table>
<tr bgcolor="lightgreen">
<td><strong> eine </strong></td></tr>
<tr bgcolor="red">
<td>schicke</td></tr>
<tr bgcolor="lightgreen">
<td><strong> bunte </strong></td></tr>
<tr bgcolor="red">
<td>Tabelle</td></tr>
</table>
</body></html>
*Bunte Tabellen sind weder schön, noch sollte man direkt mit HTML formatieren, also nur für das Beispiel
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
192 / 295
JavaEE Web Tier
Custom Tags
Fragmente & sichtbare Variablen, Beispiel Tag
table.tag
Attribute des Tags
• items: Was wird
angezeigt
1
2
3
4
5
• even: Wie werden
geradzahlige Zeilen
formatiert
• odd: . . .
Sichtbare Variablen des
Tags
6
7
8
9
10
11
12
13
14
15
• current: Der Inhalt der 16
Zeile
17
18
<%@ tag body-content="scriptless"
%><%@ attribute name="items" required="true"
%><%@ attribute name="even" fragment="true" required="true"
%><%@ attribute name="odd" fragment="true" required="true"
%><%@ variable name-given="current"
variable-class="java.lang.Object" scope="NESTED"
%><%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<table>
<c:forEach var="current" items="${items}" varStatus="status">
<c:choose><c:when test="${status.count % 2 == 0}"
><tr bgcolor="red">
<td><jsp:invoke fragment="even"/></td></tr>
</c:when><c:otherwise
><tr bgcolor="lightgreen">
<td><jsp:invoke fragment="odd"
/></td></tr></c:otherwise
></c:choose></c:forEach></table>
• Für alle Zeilen
• Wenn gerade, hole „even“-Fragment in rote,
sonst „odd“-Fragment in hellgrüner Zeile
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
193 / 295
JavaEE Web Tier
Custom Tags
Tag-Dateien Verpacken
Tag-Dateien in JAR-Datei
verpacken
Ein Tag Library Descriptor der JSTL
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
5
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
1
2
• Optional
• Verzeichnis in
JAR-Datei, beginnt mit
/META-INF/tags/
• JAR-Datei unter
/WEB-INF/lib
6
7
8
9
10
11
version="2.1">
<tlib-version>1.2</tlib-version>
<short-name>c</short-name>
<uri>http://java.sun.com/jsp/jstl/core</uri>
...
</taglib>
deployen
• Muss Tag Library
Descriptor enthalten
• Datei mit Endung
*.tld
• Beispiel:
jstl-1.2.1.jar
Prof. Dr. Peter Barth (HS-RheinMain)
Inhalt Tag Library Descriptor
• tlib-version: Eigene Versionsnummer
• Optional short-name: Vorschlag, präferiert
• uri: Eindeutiger Bezeichner
• Tag-File Elemente
Web-basierte Anwendungen
6. Juli 2015
194 / 295
JavaEE Web Tier
Custom Tags
Tag Library Descriptor – Tag-File Elemente
Tag-File Element in TLD-Datei
Tag-Files in Tag Library Descriptor
<tag-file>
2
<name>copyleft</name>
3
<path>/WEB-INF/tags/copyleft.tag</path>
4 </tag-file>
1
• Tag tag-file
• Kind name: Name des Tags,
meist Dateiname
• Kind path: Pfad zu der Tag-Datei
in der Web-Anwendung
Ablage der TLD-Datei
• TLD-Datei unter META-INF/ ablegen
innerhalb der JAR-Datei
• TLD-Datei auch ohne JAR möglich,
dann unter WEB-INF/tlds/
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
195 / 295
JavaEE Web Tier
Custom Tags
Tag-Klassen
Tag-Klassen, Alternative zu Tag-Dateien
• Java statt JSP zur Definition von Tags
Vorgehen, ähnlich zu Servlets
• Von vordefinierter Klasse TagSupport erben
• Methoden doStartTag, doInitBody, doAfterBody, doEndTag überschreiben,
Rückgabewerte bestimmen nächsten Aufruf
Einbinden
• Verpacken wie Tag-Datei mit TLD-Dateien, verpflichtend
• Verwenden wie gehabt
Alle Möglichkeiten vorhanden
• Attribute, Variablen zur Verfügung stellen
• Tag Body manipulieren
• Mehr Features, zum Beispiel Zugriff auf umgebendes Tag
Einsatz: Strukturelle Tags, zum Beispiel <c:forEach>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
196 / 295
Java Server Faces
Model 1 / Model 2 Architektur
Review – Model 1 Architektur
Nur JSPs und Beans
• Daten und Logik in Beans, Model
• JSP für die Präsentation, Views
Vorteile
• Auslagern der Datenhaltung
• Auslagern eines großen Teils der Logik
• Auslagern in Standard, Beans
JSP
Verbesserungsbedarf
JSP
JSP
JSP
• Vermischung von Anwendungslogik
und Präsentation
• Steuerung der Abfolge der Web-Seiten
in JSPs
• Hart realisierte Links . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
197 / 295
Java Server Faces
Model 1 / Model 2 Architektur
Review – Model 2 Architektur
Model 1 plus Controller
• Zentrales Servlet verarbeitet
Eingaben, Controller
• Daten und Logik in Beans, Model
• JSP für die Präsentation, Views
request
• Zusätzliche übergreifende Aspekte mit
response
Filter, z. B. Logging, Authentifizierung
Controller
Servlet
Vorteile, Model 1 plus
forward
JSP
JSP
JSP
JSP
• Vermeidet hart realisierte Links
update
• Auslagern der Ablauflogik in Servlet
retrieve
Ok, aber weiterer Verbesserungsbedarf
• Ablauflogik in Java hart realisiert
• Keine vordefinierten UI-Komponenten,
händisches HTML, kein Tooling
• Keine Events und kein Event-Handling,
Prof. Dr. Peter Barth (HS-RheinMain)
händische Anfrageauswertung
Web-basierte Anwendungen
6. Juli 2015
198 / 295
Java Server Faces
Java Server Faces – Übersicht
Java Server Faces
Ziele
• UI-Framework für interaktive Web-Anwendungen
• Wiederverwendbare UI-Komponenten nutzen und erstellen
• Datenbindung zwischen Logik (Beans) und UI-Komponenten
• Event-Modell um Browser-Events auf Server-Methoden abzubilden
• Konfigurierbare Ablaufsteuerung
• Tooling („zusammenklicken“) möglich machen
Historie
• 2003-06:
JSF 1.x, Servlet 2.5, JSP 2.1
Erfahrungen aus Struts-Framework, Fokus UI in JSF
• 2009:
JSF 2.0, Servlet 2.5/3.x, Facelets
Ajax, Partial Updates / Single-Page Anwendungen
• 2010:
JSF 2.1, Maintenance Release
• 2013-14:
JSF 2.2, Servlet 3.x, Facelets
HTML5, Faces Flows (wizards), File upload, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
199 / 295
Java Server Faces
Java Server Faces – Übersicht
Java Server Faces – Features, Architektur
Konfiguration und Folgeseitensteuerung
• Deklarative Konfiguration
• Kein Java-Code
Komponentenbaum
request
• Abstraktion der Ansicht
facelet
JSP
JSP
JSP
• Wie klassische UI-Komponente
Datenbindung
• UI-Daten und Bean-Properties
• Deklarative Assoziation
Hilfsmittel
response
render
facesconfig.
xml
Faces
Servlet
faces
Komponentenbaum
• Konvertierung, Validierung
update
• Fehlermeldungen
retrieve
• Lokalisierung
• Expression Language, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
200 / 295
Java Server Faces
Java Server Faces – Übersicht
Java Server Faces – FacesServlet
Fest vorgegebenes Controller Servlet
• FacesServlet
• Front-Controller, Teil des Frameworks
• Für alle Anwendungen gleich
request
Konfiguration separat
facelet
JSP
JSP
JSP
• faces-config.xml
• Seitenfolgesteuerung: anhand
render
Rückgabewerte von Methoden
• Beans: Initialisierung, Scope
• ...
response
facesconfig.
xml
Faces
Servlet
faces
Komponentenbaum
Features Klassischer UIs
update
• Action-Handler
retrieve
• Event-Listener
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
201 / 295
Java Server Faces
Java Server Faces – Übersicht
Java Server Faces – Komponentenbaum
Abstraktion der Präsentation / View
• Komponentenbaum
• Deklaration der
Komponentenhiearchie in Facelets
request
statt nur HTML-Ansicht
• Automatischer interner Aufbau als
Baumstruktur
response
facelet
JSP
JSP
JSP
UI-Komponenten
render
• UI-Komponenten als Widgets
• Nutzung über Tags
• Ersetzt HTML-Tags
HTML rendern eine mögliche Ansicht
facesconfig.
xml
Faces
Servlet
update
Komposition
faces
Komponentenbaum
retrieve
• Zusammenbau von Seiten aus
Teilkomponenten
• Wiederverwendbare UI-Bausteine
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
202 / 295
Java Server Faces
Java Server Faces – Übersicht
Java Server Faces – Datenbindung
Datenbindung
• Binden von Daten einer
UI-Komponente an Bean-Properties
• Bei Request werden Werte aus
request
UI-Komponente automatisch in
Bean-Property gesetzt
response
facelet
JSP
JSP
JSP
• Für Response werden Werte für
UI-Komponente automatisch aus
Bean-Property gelesen
Lebenszyklusmanagement
• Konfiguration, initiale Werte
render
facesconfig.
xml
Faces
Servlet
faces
Komponentenbaum
• Einheitlicher Namen und Sichtbarkeit
update
nur noch an einer Stelle
retrieve
• Deklaration Scope, dann
Initialisierung, halten und entfernen
automatisch
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
203 / 295
Java Server Faces
JSF einrichten
JSF Bibliotheken
JSF Implementierung
• Mojarra: Referenzimplementierung von Oracle
• Open Source, stabil, Standard
• Alle grundlegenden Komponenten
• Verwenden wir
• Später: Alternativen mit mehr Widgets und mehr
Features und mehr fancy . . . möglich
Einbinden
• Zielordner WEB-INF/lib
• Datei javax.faces-2.2.9.jar
• Nur noch eine Datei (API und Implementierung)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
204 / 295
Java Server Faces
JSF einrichten
Zentrales Servlet Konfigurieren
web.xml
Zentrales Servlet
1
• javax.faces.webapp.FacesServlet
2
3
• Front-Controller
4
5
• Klassenhierarchie und Klassenname
6
7
in JSF-Standard vorgegeben
8
• Von der konkreten
9
10
JSF-Implementierung realisiert
11
12
Konfiguration
13
14
• Servlet-Name beliebig, FacesServlet 15
<servlet>
<servlet-name>FacesServlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>FacesServlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
wird gerne genommen
• Mapping auf Dateien mit Endung .jsf
• Andere Mappings (Pfad, andere
Endungen) möglich, häufig *.xhtml
Optionale Besonderheiten
• load-on-startup, Instanz bei Start,
kein Warten bei erstem Klick
• Kontextparameter PROJECT_STAGE auf
erhöht Anzahl der
Log-Ausgaben, gut bei Entwicklung
Development
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
205 / 295
Java Server Faces
JSF einrichten
JSF-Konfiguration in faces-config.xml
faces-config.xml
• Konfigurationsdatei für Java Server Faces
• Unter WEB-INF, neben web.xml
• Initial leer außer faces-config Tag
faces-config.xml
<faces-config
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
5
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
1
2
6
version="2.2">
...
1
</faces-config>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
206 / 295
Java Server Faces
JSF einrichten
Eclipse JSF Tooling / Ermöglichen
JSF Tooling in Eclipse – verwenden wir
• Projekt-Facette JSF, Version 2.2 einrichten
• „Further Configuration Required/Available“
→ „Disable Library Configuration“
• Wir verwalten das Hinzufügen der JSF-Implementierung selbst im Projekt und nicht in
Abhängigkeit von Eclipse
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
207 / 295
Java Server Faces
JSF einrichten
Eclipse JSF Klicki Bunti
JSF Klicki Bunti in Eclipse, Manipulation faces-config.xml – vermeiden wir
• Wir editieren XML direkt (mit Completion) ohne Klicki-Bunti!!!
• Wir wählen also den rechten Tab Source
• Nur zum Anschauen sinnvoll, wer denn unbedingt will
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
208 / 295
Java Server Faces
JSF Erstes Beispiel
Java Server Faces – Erstes Beispiel
NameBean
• Wie in JSP
• Properties: vorname, mittelname,
nachname, alter
• Nur lesen Properties: name, status
form.jsf
• status prüft ob Werte gültig sind
Formulare mit Facelets
wenn gueltig ok ist
• Beschreibung des
Komponentenbaums, statt
Generierung von Markup
• Eingabeformular für Properties,
form.xhtml bei Aufruf form.jsf
• Solange im Formular bis gültig
• Dann Ausgabe
• Ausgabe mit Begrüßung, show.xhtml
bei Aufruf show.jsf
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
show.jsf
6. Juli 2015
209 / 295
Java Server Faces
JSF Erstes Beispiel
NameBean – Wiederholung
webanw.NameBean
• Normale Bean,
nichts JSF
spezifisches
• Fast wie bei
JSP-Beispiel
Unterschiede
• Rückgabewert
String
bei
gueltig
• Nur Lesen
Property
status
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class NameBean implements Serializable {
String vorname; String nachname;
String mittelname; int alter;
public NameBean() { // parameterloser Konstruktor
vorname = "Max"; mittelname="M.";
nachname = "Mustermann"; alter = 0;
}
public String getVorname() { return vorname; }
public void setVorname(String vorname) { this.vorname = vorname; }
public String getMittelname() { return mittelname; }
public void setMittelname(String mittelname) { this.mittelname = mittelname; }
public String getNachname() { return nachname; }
public void setNachname(String nachname) { this.nachname = nachname; }
public int getAlter() { return alter; }
public void setAlter(int alter) { this.alter = alter<0 ? 0 : alter; }
public String getName() { // Convenience, lesend
return vorname+" "+mittelname+" "+nachname;
}
public String gueltig() { // Logik
if (alter > 0 &&
(vorname+mittelname+nachname).length() >= 4) {
return "ok";
} else {
return "fehler";
}
}
public String getStatus() { return gueltig(); }
}
28
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
210 / 295
Java Server Faces
JSF Erstes Beispiel
Bean Registrieren
faces-config.xml
Beans registrieren
1
• Beans über Konfiguration dem
2
3
Framework bekannt geben
4
• Namen mit Instanz einer Klasse
5
6
assoziieren
7
8
• Scope festlegen
9
10
Beispiel webanw.NameBean
11
<managed-bean>
<managed-bean-name>
namebean
</managed-bean-name>
<managed-bean-class>
webanw.NameBean
</managed-bean-class>
<managed-bean-scope>
session
</managed-bean-scope>
</managed-bean>
• namebean ist Instanz von
webanw.NameBean
Vorteile
• Je Session eine Instanz für die Dauer
einer Session
• Framework verwaltet Lebenszyklus,
Vermeidung von wiederholtem
Initialisierungscode in Java
• In Facelet-Seite keine wiederholte
Deklaration der Bean notwendig
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
211 / 295
Java Server Faces
JSF Erstes Beispiel
Bean Registrieren – Alternative mit Annotationen
NameBean.java
Annotationen
package webanw;
2 import javax.faces.bean.*;
1
• Direkt an der Bean Klasse
3
• @ManagedBean
4
• optionales Attribut name statt
5
6
managed-bean-name
7
• Default Name ist Klassenname, aber
8
erster Buchstabe klein, im Beispiel
9
@ManagedBean(name="namebean")
@SessionScoped
public class NameBean
implements Serializable {
....
}
nameBean
Vorteile
• @SessionScoped
• Alles an einem Ort, einfacher
• Andere Scopes verfügbar
• Philosophie seit JavaEE 6
Annotation und faces-config.xml
• Bei zusätzlicher Konfiguration in
faces-config.xml überschreibt diese
die Annotationen
Prof. Dr. Peter Barth (HS-RheinMain)
• Bean wird abhängig von JSF
• Bei Datenbeans kritisch, bei
• Verwendung der Annotationen als
Default Werte
Nachteile
„Backingbeans“ ok
Verwendung in Ordnung
Web-basierte Anwendungen
6. Juli 2015
212 / 295
Java Server Faces
JSF Erstes Beispiel
Navigationsregel zur Folgeseitensteuerung
faces-config.xml
Navigationsregeln
• Folgeseitensteuerung mit Strings
• Explizit oder aus Rückgabewerten von
Methodenaufrufen
1
2
3
4
5
6
• Festlegung Folgeseite per
7
<navigation-rule>
<from-view-id>/form.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/show.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Konfiguration, ohne Java Code
• Identifikation der Seite über (innerhalb
der Anwendung) absoluten Pfad der
XHTML-Seite
Beispiel
Ergebnis ok zurückgegeben wird
• dann gehe zur Seite /show.xhtml
• Wenn nicht, dann auf der Seite bleiben
Prof. Dr. Peter Barth (HS-RheinMain)
• Java Code unabhängig von Navigation
• View unabhängig von Navigation
Alternative ohne Navigationsregel
• Wenn von der Seite /form.xhtml das
(Default, aber mit Warnung)
Vorteile
• Outcome String ist nächste Seite
• Im Beispiel show oder /show.xhtml
• Outcome null
→ auf gleicher Seite
bleiben
• Vermeiden
Web-basierte Anwendungen
6. Juli 2015
213 / 295
Java Server Faces
JSF Erstes Beispiel
Grober Ablauf Name-Beispiel
Anfrage form.jsf
• Endung .jsf
• Wegen web.xml-Konfiguration auf
FacesServlet
umleiten
request
FacesServlet
response
form.xhtml
• form.xhtml ist zugehöriges Facelet
• Komponentenbaum aus form.xhtml
render
• Aktualisierung UI-Komponenten/Bean
aus Anfrageparameter
• Festlegung Folgeseite
facesconfig.
xml
Faces
Servlet
Komponentenbaum
Antwort
update
• HTML-Seite rendern aus
retrieve
Komponentenbaum
• Aktualisierte Werte aus Beans in
namebean
Komponentenbaum
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
214 / 295
Java Server Faces
JSF Erstes Beispiel
Facelets – Rahmen XHTML-Seite
1
2
3
4
5
6
7
8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Der Titel</title>
9
10
11
</h:head>
<h:body>
12
13
14
15
</h:body>
</html>
• XHTML, Fokus auf Festlegung Komponentenbaum, nicht HTML-Generierung
• Tag-Libraries von JSF einbinden:
• h: HTML-Ersatz, f: Core JSF, ui: Facelet-spezifisch
• Statt xmlns.jcp.org häufig noch java.sun.com
• <h:head> und <h:body> erlauben Framework einzugreifen
• Seitenspezifische Inhalte innerhalb <h:head> und <h:body>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
215 / 295
Java Server Faces
JSF Erstes Beispiel
Anzeigeseite Facelet show.xhtml
show.xhtml
1
2
3
4
5
6
7
8
9
10
11
<h:body>
<h3>Eingabe akzeptiert</h3>
Hallo <h:outputText value="#{namebean.name}" />.<br />
Status der Eingabe #{namebean.status}
<h:panelGrid columns="2">
<h:outputText value="Vorname" /> <h:outputText value="#{namebean.vorname}" />
<h:outputText value="Mittelname" /> <h:outputText value="#{namebean.mittelname}" />
<h:outputText value="Nachname" /> <h:outputText value="#{namebean.nachname}" />
<h:outputText value="Alter" /> <h:outputText value="#{namebean.alter}" />
</h:panelGrid>
</h:body>
• <h:outputText> für Ausgabe
kann weggelassen werden, sollte aber
verwendet werden
• Zugriff auf Properties mit #, inzwischen
$
wieder erlaubt, aber bei # bleiben
• <h:panelGrid> als erstes UI-Widget,
zweispaltiges Layout
Angezeigte URL bei JSF meist ohne Bedeutung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
216 / 295
Java Server Faces
JSF Erstes Beispiel
Eingabeseite Facelet form.xhtml
form.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
<h:body>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Vorname" /> <h:inputText value="#{namebean.vorname}" />
<h:outputLabel value="Mittelname" /> <h:inputText value="#{namebean.mittelname}" />
<h:outputLabel value="Nachname" /> <h:inputText value="#{namebean.nachname}" />
<h:outputLabel for="age" value="Alter" />
<h:inputText id="age" value="#{namebean.alter}" />
</h:panelGrid>
<h:commandButton action="#{namebean.gueltig}" value="Abschicken" />
</h:form>
</h:body>
• <h:form>, notwendig bei Eingaben,
wird gerne vergessen
• <h:inputText> für Text-Eingabezeile
• <h:outputLabel> als Info für Eingaben,
Assoziation über for und id Attribute
• <h:commandButton> statt submit,
in action als
gerufene Methode, String als Outcome
namebean.gueltig()
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
217 / 295
Java Server Faces
JSF Erstes Beispiel
Komponentenbaum aus form.xhtml
generiertes HTML aus form.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body><form id="j_idt6" name="j_idt6" method="post" action="/jsf/form.jsf" enctype="application/x-www-form
<input type="hidden" name="j_idt6" value="j_idt6">
<table><tbody>
<tr><td><label>Vorname</label></td>
<td><input type="text" name="j_idt6:j_idt9" value="Susi"></td></tr>
<tr><td><label>Mittelname</label></td>
<td><input type="text" name="j_idt6:j_idt11" value="S."></td></tr>
<tr><td><label>Nachname</label></td>
<td><input type="text" name="j_idt6:j_idt13" value="Sinnlos"></td></tr>
<tr><td><label for="j_idt6:age">Alter</label></td>
<td><input id="j_idt6:age" type="text" name="j_idt6:age" value="17"></td></tr>
</tbody></table>
<input type="submit" name="j_idt6:j_idt15" value="Abschicken">
<input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0"
value="-3026645100859955489:5107878963598123976" autocomplete="off">
</form></body>
form:6
Komponentenbaum, auf Server
• Automatische Vergabe IDs
• Ausgabeelemente werden ignoriert
• Anspringen zentrales Servlet
input:9
submit:15
• Flacher Pfad für HTML-Ausgabe
Prof. Dr. Peter Barth (HS-RheinMain)
input:11 input:13 input:17
Web-basierte Anwendungen
6. Juli 2015
218 / 295
Java Server Faces
JSF Erstes Beispiel
Optionale Darstellung von UI-Komponente
Problem: UI-Komponente ausblenden
• UI-Komponente je nach Wert angezeigen oder nicht
• In JSTL mit <c:if>, <c:if> verboten in JSF
• Änderung würde andere ID-Vergabe in
Komponentenbaum erzwingen
• Chaos beim Aktualisieren
Lösung
• rendered Attribut: Vorgabe true, ausschalten false
• UI-Komponente ist dann nicht sichtbar, aber immer
noch vorhanden
• Gleiche IDs, gleicher Komponentenbaum
Beispiel: Alter nicht anzeigen bei Vorname Susanne
<h:outputLabel for="age" value="Alter"
rendered="#{namebean.vorname eq ’Susanne’ ? false : true}"/>
<h:inputText id="age" value="#{namebean.alter}"
4
rendered="#{namebean.vorname eq ’Susanne’ ? false : true}"/>
1
2
3
Allgemein: Kein JSTL in JSF verwenden!
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
219 / 295
Java Server Faces
JSF Erstes Beispiel
Navigationsregeln
faces-config.xml
<navigation-rule>
1
• Haupt Tag
2
3
<from-view-id>
4
• Ausgangsseite
5
• Absoluter Pfad in Web-Anwendung
7
6
faces-config.xml
• Pattern (/*) erlaubt
1
<navigation-case>
2
• Fallunterscheidung
3
• Optional und falls von Methode
5
<from-action>,
vermeiden
• Falls <from-outcome>
• Dann gehe zu <to-view-id>
Abarbeitung
• Von oben nach unten
<navigation-rule>
<from-view-id>/form.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/show.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
4
6
7
8
9
10
11
12
13
14
15
<navigation-rule>
<from-view-id>/form.xhtml</from-view-id>
<navigation-case>
<from-action>#{namebean.gueltig}</from-action>
<from-outcome>fehler</from-outcome>
<to-view-id>/form.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/*</from-view-id>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
• Erster Treffer
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
220 / 295
Java Server Faces
JSF Erstes Beispiel
Navigation – Festlegung der Folgeseite
Abhängig von Outcome, action-Attribut
Statisch
• Statisch oder dynamisch
1
• Erst Navigationsregel,
dann Wert + .xhtml direkt als Seite
3
2
oder bleibt (Warnung)
4
5
6
7
<h:form>
...
<h:commandButton
action="ok"
value="Abschicken" />
...
</h:form>
Statischer Outcome
• Angabe String im action-Attribut
Dynamisch
• Immer gleiche Seite/Regel
• Beispiel: Knopf drücken
1
2
→ ok
3
4
Dynamischer Outcome
5
• Angabe Methode im action-Attribut
6
7
<h:form>
...
<h:commandButton
action="#{namebean.gueltig}"
value="Abschicken" />
...
</h:form>
• Rückgabewert der Methode
• Beispiel:
→ Ergebnis von
namebean.gueltig()
Prof. Dr. Peter Barth (HS-RheinMain)
„Method-Binding“ ohne Klammern/Parameter
„Method-Expression“ mit . . . vermeiden
Web-basierte Anwendungen
6. Juli 2015
221 / 295
Java Server Faces
JSF Erstes Beispiel
Fehlermeldungen
Anforderungen
• Formular überprüfen
• Fehlermeldungen passend anzeigen
Validierung
• Eingabefelder im Tag-Body
• <f:validate...>
• Beispiel: Wert zwischen 0 und 128
Messages
<h:outputLabel for="age" value="Alter" />
<h:inputText id="age" value="#{namebean.alter}">
3
<f:validateLongRange minimum="0" maximum="128" />
4 </h:inputText>
1
• <h:messages /> zeigt alle
2
Fehlermeldungen an
• Sonst nur im Log
• Alternative während Entwicklung,
PROJECT_STAGE
auf Development in
web.xml
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
222 / 295
Java Server Faces
JSF Erstes Beispiel
Fehlermeldungen an Passender Stelle
1
Fehlermeldung an passender Stelle
2
3
• Fehlermeldung im Formular
4
• An passender Stelle
5
• Beispiel: Neben dem falschen Wert
7
6
Vorgehen
8
9
10
• Dritte Spalte aufmachen
11
12
• Wenn kein Fehler, dann leer lassen
13
14
• Verknüpfen über id
• <h:message for=... />
Prof. Dr. Peter Barth (HS-RheinMain)
15
16
<h:panelGrid columns="3">
<h:outputLabel value="Vorname" />
<h:inputText value="#{namebean.vorname}" />
<h:column />
<h:outputLabel value="Mittelname" />
<h:inputText value="#{namebean.mittelname}" />
<h:column />
<h:outputLabel value="Nachname" />
<h:inputText value="#{namebean.nachname}" />
<h:column />
<h:outputLabel for="age" value="Alter" />
<h:inputText id="age" value="#{namebean.alter}">
<f:validateLongRange minimum="0" maximum="128" />
</h:inputText>
<h:message for="age" />
</h:panelGrid>
Web-basierte Anwendungen
6. Juli 2015
223 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
Ablauf einer JSF-Anfrage
Anfragebehandlung
stoppen*
Komponentenbaum (wieder-)
herstellen
Werte UI-Komp.
mit Anfrageparam.
aktualisieren
direkt rendern
Antwort
generieren,
rendern
Anwendung
ausführen
(action)
Konvertierung
und
Validierung
Fehler
direkt rendern
Werte in Beans
aktualisieren
(setProperty)
Anfragebehandlung
stoppen*
* wurde anderweitig
behandelt, z.B. forward
Ereignisbehandlung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
224 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
JSF-Ablauf – Komponentenbaum
Komponentenbaum (wieder-)herstellen
• Seite zum ersten Mal angefragt?
• Für <name>.jsf die Facelet-Sicht <name>.xhtml
• Aus Facelet den Komponentenbaum erstellen, keine Anzeige
• Vergabe der IDs und Aufbau der inneren Struktur
• Registrieren von Events und Validatoren
• Mit Werten bestücken
• Seite schon einmal angefragt?
• Richtigen Komponentenbaum heraussuchen
Werte aktualisieren
• Anfrage-Parameter auslesen
• Passende Werte in jeweilige UI-Komponente setzen (submitted value), für den
ganzen Baum
• Konvertierung und Validierung für Komponenten mit immediate-Flag
• Sinnvoll für Änderungen die passieren sollen obwohl Formular insgesamt noch nicht
funktioniert
• Beispiel: Auswahl eines Landes, dazu passend Städte, obwohl Alter noch nicht stimmt
• Falls etwas schief geht, merken und Status auf ungültig
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
225 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
JSF-Ablauf – Werte Konvertieren und Validieren
Jede UI-Komponente validiert und konvertiert ihren Wert
• Aufruf registrierter oder automatischer Konvertierer
• Beispiel: Automatischer Konvertierer von String nach Integer oder Datum
• Aufruf registrierter oder automatischer Validierer
• Beispiel: Alter zwischen 0 und 128
• Bei Fehler wird Fehlermeldung (Message) in Fehlerliste eingetragen
• Bei Fehlern wird die Anfragebehandlung gestoppt und direkt zur Ausgabe
gesprungen
Werte sind nur im Komponentenbaum
• Die Werte sind nur in den UI-Komponenten im Komponentenbaum
• Die Werte sind noch nicht in den (Backing-)Beans
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
226 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
JSF-Ablauf – Werte in den Beans aktualisieren, Aktion
Werte aktualisieren
• Voraussetzung, alles konvertiert und validiert
• Setter der an die UI-Komponenten gebundenen
Properties der Beans anwenden
• Data-binding: #{bean.property}
• Beispiel: #{namebean.vorname}
• Aufruf in Beispiel: namebean.setVorname("Susi")
• Bei Fehler Abbruch
• Es sollte kein (Eingabe-) Fehler geben
• Eingabevalidierung in der vorherigen Phase machen
• Nur dann ist Werteaktualisierung ganz oder gar nicht
Anwendung ausführen
• Aktion, die an Ereignis gebunden ist ausführen
• Verarbeitungslogik in Methoden
• Meist im action-Attribut angegeben
• Beispiel: action="#{namebean.gueltig}"
• Arbeitet auf aktualisierten Werten in den (Backing-)Beans
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
227 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
JSF-Ablauf – Antwort Generieren
Aktuellen Stand des passenden Komponentenbaums ausgeben
• Voraussetzung: Werte in UI-Komponenten sind aktualisiert
mit Werten aus Beans oder bei Fehler „submitted value“
• Durchlauf des Baums
• Je UI-Komponente wird (HTML-)Renderer aufgerufen
Fertig generierte Seite an Browser schicken
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
228 / 295
Java Server Faces
Ablauf einer JSF-Anfrage
Ablage der Daten für Komponentenbaum
faces-config.xml
Auf dem Server
<!-2 <context-param>
3
<param-name>
4
javax.faces.STATE_SAVING_METHOD
1
• Vorgabe, empfohlen
• Komponentenbaum wird aufgebaut
5
und auf Server mit Werten vorgehalten 6
7
• Werte immer wieder aktualisiert
8
• Werte sicher auf dem Server, braucht
Speicher
Vorteil
Auf dem Client
• Weniger Speicher auf dem Server
• Alternative, wenn, dann nur bei
• Einfach zu löschen (Entwicklung)
Entwicklung
Nachteil
• Werte des Komponentenbaums
werden auf dem Client (hidden fields)
gespeichert
• Füllen des Komponentenbaums bei
• Werte sichtbar (Quelltext, Browser)
• Datenübertragung
• Hoher Aufwand (Serialisierung,
Base64, optional Verschlüsselung)
Anfrage
• Einfach zu löschen (Entwicklung)
Prof. Dr. Peter Barth (HS-RheinMain)
9
10
</param-name>
<param-value>
client
</param-value>
</context-param>
-->
Vermeiden
Web-basierte Anwendungen
6. Juli 2015
229 / 295
Java Server Faces
Aktionen und Kommandos
Aktionen und Kommandos
1
Aktion
2
3
• Binden einer Schaltfläche oder eines
4
Links an Aktion
5
6
• Methode ohne Parameter mit
7
8
Rückgabe String; oder nur ein String
9
10
• String wegen Navigationsregeln
Darstellung Kommandos – statt Submit
1
• <h:commandButton>
2
• <h:commandLink>
4
3
5
• Darstellung mit Bild möglich
6
7
Beispiel
<h:form>
<h:commandButton action="#{bb.kommando}"
value="commandButton"/> <br />
<h:commandLink action="#{bb.kommando}"
value="commandLink"/> <br />
<h:commandLink action="#{bb.kommando}">
<h:graphicImage alt="Knopf"
url="/button.png"/>
</h:commandLink> <br />
</h:form>
public String kommando() {
FacesContext fc = FacesContext.getCurrentInstance()
FacesMessage msg =
new FacesMessage("kommando ausgeführt");
fc.addMessage("kommando", msg);
return "weiter";
}
• Schaltknopf, Textlink, Imagelink
• An Methode kommando von bb
gebunden
• Aufruf bb.kommando(), Rückgabe String
Prof. Dr. Peter Barth (HS-RheinMain)
Instanz einer passenden Bean unter dem Namen bb
vorausgesetzt
Web-basierte Anwendungen
6. Juli 2015
230 / 295
Java Server Faces
Aktionen und Kommandos
ActionListener
1
Problem
2
• action, rufendes Widget unbekannt
3
4
• Parameterübergabe nicht erlaubt
5
• Man will aber je nach Widget
7
6
8
unterschiedlich reagieren
Lösung
1
2
• ActionListener
3
4
• wie bei klassischen GUIs
5
6
• Separates Tag-Attribut
7
8
• Spezielle Methode, Übergabe
ActionEvent-Parameter,
<h:form>
<h:commandButton id="weiter"
actionListener="#{bb.handleKommando}"
value="commandButton-ActionListener"/>
<h:commandButton id="ende"
actionListener="#{bb.handleKommando}"
value="commandButton-ActionListener"/>
</h:form>
public void handleKommando(ActionEvent e) {
UIComponent cmd = e.getComponent();
FacesContext fc =
FacesContext.getCurrentInstance();
String s = "handleKommando mit " + cmd.getId();
FacesMessage msg = new FacesMessage(s);
fc.addMessage("handleKommando", msg);
}
keine
Rückgabe
• Parameter vom Framework gesetzt
• Über Parameter rufendes Widget
bei Klick auf linken Button
Beispiel: Nachricht mit ID
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
231 / 295
Java Server Faces
Aktionen und Kommandos
ActionListener, Backing-Bean, Ausdrücke
1
Backing-Bean
2
3
• Durch ActionEvent-Parameter
4
Abhängigkeit von JSF
5
6
• Unterscheidung „Backing“-Beans und
7
8
Daten/Logik-Beans
public void handleKommando(ActionEvent e) {
UIComponent cmd = e.getComponent();
FacesContext fc =
FacesContext.getCurrentInstance();
String s = "handleKommando mit " + cmd.getId();
FacesMessage msg = new FacesMessage(s);
fc.addMessage("handleKommando", msg);
}
• Für Backing-Beans, die UI
stützen/backen, ist Abhängigkeit OK
FacesContext
• Für Kommunikation mit Framework
• Aktuelle Instanz mit statischer
Methode getCurrentInstance(), wenn
im JSF-Ablauf aufgerufen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
232 / 295
Java Server Faces
Aktionen und Kommandos
Ausdrücke in Java Auflösen
Problem
Expression Language Resolver
• Ausdrücke in Java auflösen
1
• Notwendig, um auf registrierte Beans
3
2
4
über Namen zuzugreifen
5
6
Lösung – zwei Alternativen
• Mit Expression Language Resolver,
evaluateExpressionGet
Cast notwendig
1
• Mit evaluateExpressionGet und
2
3
Angabe der Ziel-Klasse
4
5
Beispiel
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
ELResolver elr = app.getELResolver();
DataModelBackingBean dmbb;
dmbb = (DataModelBackingBean)
elr.getValue(fc.getELContext(), null, "dmbb");
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
DataModelBackingBean dmbb;
dmbb = app.evaluateExpressionGet(fc,
"#{dmbb}", DataModelBackingBean.class);
• Eine Bean-Klasse
DataModelBackingBean
• Eine „Managed Bean“ Instanz unter
dem Namen dmbb
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
233 / 295
Java Server Faces
UI-Komponenten
UI-Komponentenhierarchie, Auszug
UIComponent
UIComponentBase
UIGraphic
UICommand
UIData
UIPanel
UIOutput
<h:commandButton>
UIInput
UISelectOne
unabhängig von Anzeige
HTMLCommandButton
HTMLInputHidden
HTMLCommandLink
HTMLInputSecret
HTMLSelect
OneListBox
HTMLInputText
Rendertypen
Image
Button
Link
Prof. Dr. Peter Barth (HS-RheinMain)
Table
Grid
Hidden
ListBox
Group
Secret
Menu
Text
Radio
Web-basierte Anwendungen
6. Juli 2015
234 / 295
Java Server Faces
UI-Komponenten
Styling
Default Renderer HTML
• Standard HTML-Attribute meist
erlaubt
1
2
3
4
5
6
<style type="text/css">
.roteklasse {
background-color: #FF6666;
font-weight: bold;
}
</style>
• CSS verwenden stark
empfohlen
1
Beispiel
2
3
• mit <h:outputText>, geht auch
mit anderen Tags
• style-Attribut: direkter Einfluss,
vermeiden
4
5
6
7
8
9
<h:panelGrid columns="1">
<h:outputText value="Hallo Welt" />
<h:outputText style="background-color:#FF6666;"
value="Hallo rote Welt" />
<h:outputText styleClass="roteklasse"
value="Hallo rote fette Welt mit class" />
<h:outputText styleClass="roteklasse"
value="#{bb.zeichenkette}" />
</h:panelGrid>
• styleClass-Attribut: Angabe der
CSS-Klasse fürs Rendern
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
235 / 295
Java Server Faces
UI-Komponenten
Texteingabe
1
Optionen für Texteingabe
2
3
• <h:inputText>: Eingabe Text
4
• <h:inputSecret>: Eingabe Passwort
5
• <h:inputTextarea>: Eingabe längerer
7
Text
6
8
9
10
11
<h:form>
<h:panelGrid columns="2">
<h:outputText value="inputText"/>
<h:inputText id="i1" value="#{bb.zk1}" />
<h:outputText value="inputSecret"/>
<h:inputSecret id="i2" value="#{bb.zk2}" />
<h:outputText value="inputText"/>
<h:inputTextarea id="i3" value="#{bb.zk3}" />
<h:commandButton value="mach"/>
</h:panelGrid>
</h:form>
12
<h:panelGrid border="1" columns="1">
<h:outputText value="inputText: #{bb.zk1}"/>
15
<h:outputText value="inputSecret: #{bb.zk2}"/>
16
<h:outputText value="inputTextarea: #{bb.zk3}"/>
17 </h:panelGrid>
13
14
BackingBean.java
private String s1, s2, s3;
2 public String getZk1() { return s1; }
3 public void setZk1(String s) { this.s1 = s; }
1
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
236 / 295
Java Server Faces
UI-Komponenten
Facetten
1
<h:facet>
2
3
• Zusätzliche Informationen in
4
Vater-Element rendern
5
6
• Für Wiederholungen in zum Beispiel
Tabellenköpfen oder Gruppierungen
wie <h:panelGrid>
7
8
9
10
11
Beispiel
12
• Schönere Ausgabe der Texteingabe
13
14
• Zweispaltig
15
• Mehrspaltige Überschrift grau
17
hinterlegt
16
18
19
<style type="text/css">
.gray {
background-color: #AAAAAA;
font-weight: bold;
}
</style>
Die Werte sind <br />
<h:panelGrid columns="2">
<f:facet name="header">
<h:outputText styleClass="gray"
value="Eine Ausgabebox" />
</f:facet>
<h:outputLabel value="inputText:" />
<h:outputText value="#{bb.zk1}" />
<h:outputLabel value="inputSecret:" />
<h:outputText value="#{bb.zk2}" />
<h:outputLabel value="inputTextarea:" />
<h:outputText value="#{bb.zk3}" />
</h:panelGrid>
Bei panelGrid ist Facette Überschrift über
alle Spalten.
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
237 / 295
Java Server Faces
UI-Komponenten
Ja/Nein Auswahl
1
<h:selectBooleanCheckbox>
2
• Ja/Nein Auswahl
3
• Checkbox
5
• Binden an einen Booleschen Wert
7
4
6
8
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Ja oder Nein" />
<h:selectBooleanCheckbox value="#{bb.jaNein}"/>
<h:outputText value="Wert ist #{bb.jaNein}" />
<h:commandButton value="mach"/>
</h:panelGrid>
</h:form>
BackingBean.java
1
2
3
4
5
6
7
Prof. Dr. Peter Barth (HS-RheinMain)
private Boolean jaNein;
public Boolean getJaNein() {
return jaNein;
}
public void setJaNein(Boolean jaNein) {
this.jaNein = jaNein;
}
Web-basierte Anwendungen
6. Juli 2015
238 / 295
Java Server Faces
UI-Komponenten
Einfachauswahl Statisch
<h:selectOneRadio>
1
2
• Ergebnis in String-Property
3
• Möglichkeiten manuell angeben
5
mit <f:selectItem> innerhalb
von Tag
<f:selectItem>
4
6
7
8
9
10
11
• itemValue: Wert für Property
12
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Einfachauswahl" />
<h:selectOneRadio value="#{bb.zk1}">
<f:selectItem itemValue="Eins" itemLabel="One" />
<f:selectItem itemValue="Zwei" itemLabel="Two" />
<f:selectItem itemValue="Drei" itemLabel="Three" />
</h:selectOneRadio>
<h:outputText value="Wert ist #{bb.zk1}" />
<h:commandButton value="mach"/>
</h:panelGrid>
</h:form>
• itemLabel: Ansicht im Browser
Alternativen
• <h:selectOneListbox>
• <h:selectOneMenu>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
239 / 295
Java Server Faces
UI-Komponenten
Einfachauswahl dynamisch
<h:select*>
• Wie statisch
<f:selectItems>
• Property, Value-Bindung
1
2
3
4
5
6
7
8
• Liste mit SelectItem-Instanzen
9
10
Beispiel
11
12
• Wie statisch
• Visualisierung Ausrichtung
BackingBean.java
1
2
3
4
5
6
7
Prof. Dr. Peter Barth (HS-RheinMain)
<h:form>
<h:panelGrid columns="2">
<h:outputText
value="Einfachauswahl mit Listenproperty" />
<h:selectOneRadio layout="pageDirection"
value="#{bb.zk2}">
<f:selectItems value="#{bb.selectListe}" />
</h:selectOneRadio>
<h:outputText value="Wert ist #{bb.zk2}" />
<h:commandButton value="mach"/>
</h:panelGrid>
</h:form>
public List<SelectItem> getSelectListe() {
List<SelectItem> ret = new ArrayList<SelectItem>();
ret.add(new SelectItem("Eins", "One"));
ret.add(new SelectItem("Zwei", "Two"));
ret.add(new SelectItem("Drei", "Three"));
return ret;
}
Web-basierte Anwendungen
6. Juli 2015
240 / 295
Java Server Faces
UI-Komponenten
Mehrfachauswahl
<h:selectManyListbox>
1
2
• Wie One . . .
3
• . . . aber Ergebnis in Property
5
vom Typ String[] oder
List<String>
4
6
7
8
9
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Mehrfachauswahl" />
<h:selectManyListbox value="#{bb.selectLErg}">
<f:selectItems value="#{bb.selectListe}"/>
</h:selectManyListbox>
<h:commandButton value="mach"/>
</h:panelGrid>
</h:form>
BackingBean.java
1
2
3
4
5
6
7
private List<String> selectLErg;
public List<String> getSelectLErg() {
return selectLErg;
}
public void setSelectLErg(List<String> erg) {
selectLErg = erg;
}
Alternativen
• <h:selectManyCheckbox>
• <h:selectManyMenu>
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
241 / 295
Java Server Faces
UI-Komponenten
Tabelle aus Daten
<h:dataTable>
1
2
• Tabelle aus dynamischen Daten
3
• Mehre Items, meist Liste oder
5
Feld über value-Attribut binden
• Laufvariable über var-Attribut
• <c:forEach> nicht verwenden
<h:column>
• Je Spalte ein Tag
4
6
7
8
9
10
11
12
13
14
<h:dataTable var="wahl" value="#{bb.selectLErg}">
<h:column>
<f:facet name="header">
<h:outputText value="Text"/>
</f:facet>
<h:outputText value="Wahl ist" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Wert"/>
</f:facet>
<h:outputText value="#{wahl}" />
</h:column>
</h:dataTable>
• In Tag Zugriff auf Laufvariable
Beispiel
• Spalten mit Kopf (Facetten)
• Text und Wert
<h:panelGroup>, um mehrere Komponenten zu
verwenden, wo nur eine erwartet wird
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
242 / 295
Java Server Faces
Interaktion mit Kollektionen
Interaktion mit Kollektionen – DataModel
Ziel – Interaktion mit mehreren Objekten
• Bearbeiten eines Objekts in einer
Sammlung von Objekten
• Herausfinden welches Objekt gemeint
ist
DataModel
• JSF-Typ für Datenmodell auf
dataModel
Java-Ebene
wrappedData
• Arbeitet mit <h:dataTable>
rowCount
automatisch zusammen
rowIndex
n
2
rowData
• Abstrakter Typ, verschiedene
Unterklassen, z.B. ListDataModel
• Generischer Typ, sollte mit Datentyp
parametrisiert werden
item1
item2
item3 ... itemn
• Beinhaltet Objekt-Sammlung
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
243 / 295
Java Server Faces
Interaktion mit Kollektionen
Beispiel – Auswahl, Editieren, Sichern
Beispiel
• Auswahl: Wähle ein Element aus
einer Sammlung von Elementen
• Editieren: Ausgewähltes
Element editieren, Kopieren in
temporäres zu editierendes
Objekt
• Sichern: Daten aus geändertem
Objekt in Original-Objekt zurück
speichern, dadurch Abbrechen
möglich
Umsetzung mit DataModel
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
244 / 295
Java Server Faces
Interaktion mit Kollektionen
Backing-Bean für Interaktion – DataModelBackingBean
DataModelBackingBean
• Liste von NameBean
@ManagedBean(name="dmbb")
@SessionScoped
3 public class DataModelBackingBean implements Serializable {
1
2
• Passendes DataModel
private List<NameBean> lnb;
// DataModel ist nicht serialisierbar
3 transient private DataModel<NameBean> namesModel;
4 private NameBean current = null;
5 private NameBean currentCopy = null;
1
• Referenz auf ausgewähltes
Element
• Referenz auf Kopie des
2
ausgewählten Elements
Initialisierung, Getter
• Daten besorgen und für Dauer
der Interaktion merken
public DataModelBackingBean() {
lnb = retrieveData();
3
namesModel = new ListDataModel<NameBean>();
4
namesModel.setWrappedData(lnb);
5 }
1
2
• Passende Getter zum Zugriff
...
public List<NameBean> getListe() {
return lnb;
3 }
4 public DataModel<NameBean> getNamesModel() {
5
return namesModel;
6 }
7 public NameBean getCurrentCopy() {
8
return currentCopy;
Web-basierte
Anwendungen
6. Juli 2015
9 }
1
2
Prof. Dr. Peter Barth (HS-RheinMain)
245 / 295
Java Server Faces
Interaktion mit Kollektionen
Darstellung und Auswahl
1
Darstellung
2
• Mehrspaltige (column) Tabelle
• Laufvariable name
4
5
6
7
Auswahl
• Attribut dername
3
→ aktuelles
NameBean-Objekt
• Attribut der aktuellen
UI-Komponente commandLink
• Auslesen über ActionEvent
1
2
3
4
5
6
7
8
<h:dataTable var="name" value="#{dmbb.namesModel}">
<h:column>
<f:facet name="header">
<h:outputText value="Vorname" />
</f:facet>
<h:outputText value="#{name.vorname}" />
</h:column>
<h:column>
<h:commandLink actionListener="#{dmbb.select}"
action="edit">
<f:attribute name="dername" value="#{name}" />
<h:outputText value="Edit" />
</h:commandLink>
</h:column>
</h:dataTable>
DataModelBackingBean.java
1
2
3
4
5
Prof. Dr. Peter Barth (HS-RheinMain)
public void select(ActionEvent ae) {
current = (NameBean)
ae.getComponent().getAttributes().get("dername");
currentCopy = new NameBean(current);
}
Web-basierte Anwendungen
6. Juli 2015
246 / 295
Java Server Faces
Interaktion mit Kollektionen
Editieren Kopie einer NameBean
Editieren
• Auf aktueller Kopie currentCopy
1
2
3
4
• Abbrechen ohne Aktion
5
• Sichern mit Methode save
7
6
8
9
10
11
12
13
14
Prof. Dr. Peter Barth (HS-RheinMain)
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Vorname" />
<h:inputText value="#{dmbb.currentCopy.vorname}" />
<h:outputLabel value="Mittelame" />
<h:inputText value="#{dmbb.currentCopy.mittelname}" />
<h:outputLabel value="Nachname" />
<h:inputText value="#{dmbb.currentCopy.nachname}" />
<h:outputLabel value="Alter" />
<h:inputText value="#{dmbb.currentCopy.alter}" />
</h:panelGrid>
<h:commandButton action="cancel" value="Abbrechen" />
<h:commandButton action="#{dmbb.save}" value="Sichern" />
</h:form>
Web-basierte Anwendungen
6. Juli 2015
247 / 295
Java Server Faces
Interaktion mit Kollektionen
Editiere Kopie Sichern
DataModelBackingBean.java
Sichern
1
• Werte der Kopie in aktueller
Bean speichern
• „Abbrechen“ ändert nur Kopie,
nicht zurückspeichern
2
3
4
5
6
7
8
9
10
11
12
13
14
Prof. Dr. Peter Barth (HS-RheinMain)
public String save() {
String result = currentCopy.gueltig();
if (result.equals("ok")) {
current.setVorname(currentCopy.getVorname());
current.setMittelname(currentCopy.getMittelname());
current.setNachname(currentCopy.getNachname());
current.setAlter(currentCopy.getAlter());
} else {
FacesContext fc = FacesContext.getCurrentInstance();
FacesMessage msg = new FacesMessage("Objekt nicht gültig
fc.addMessage("edit", msg);
}
return result;
}
Web-basierte Anwendungen
6. Juli 2015
248 / 295
Java Server Faces
Interaktion mit Kollektionen
Editieren/Sichern – Navigationsregeln
faces-config.xml
Navigationsregeln von selectname
• selectname
edit
−→
1
2
editname
3
4
5
6
Navigationsregeln von editname
• editname
• editname
• editname
−→ selectname
cancel
−→
fehler
−→
7
8
ok
9
10
selectname
11
editname,
13
bleibt
12
14
15
16
17
18
19
20
21
22
Prof. Dr. Peter Barth (HS-RheinMain)
<navigation-rule>
<from-view-id>/selectname.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/editname.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/editname.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/selectname.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>cancel</from-outcome>
<to-view-id>/selectname.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>fehler</from-outcome>
<to-view-id>/editname.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Web-basierte Anwendungen
6. Juli 2015
249 / 295
Java Server Faces
Interaktion mit Kollektionen
Blättern
Blättern – Pager
• Anzeige von begrenzt vielen
Elementen je Seite
• Vor, Zurück, Start, Ende
Beispiel
• 25 Elemente, 10 zeigen
• Vor, Vor, Zurück
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
250 / 295
Java Server Faces
Interaktion mit Kollektionen
Blättern – Pager
Separate Klasse – Pager
• first: Erstes anzuzeigendes
Element
public class
private int
3
private int
4
private int
1
2
5
6
• size: Anzahl der Elemente
insgesamt
• noRows: Wieviel Elemente sollen
ab first angezeigt werden
• Methoden zum Navigieren
Einbettung
DataModelBackingBean.java
7
8
9
10
11
12
13
14
15
16
17
18
19
private Pager pager = new Pager();
2 public Pager getPager() { return pager; } 20
1
21
22
23
24
25
26
Prof. Dr. Peter Barth (HS-RheinMain)
Pager implements Serializable {
first;
size; // wie viele
noRows; // davon wie viele zeigen
public Pager() {
first = 0; size = 0; noRows = 10;
}
public int getFirst() { return first; }
public int getSize() { return size; }
public void setSize(int size) {
this.size = size; this.first = 0;
}
public int getNoRows() { return noRows; }
public void setNoRows(int noRows) { this.noRows = noRows; }
public String start() { first = 0; return null; }
public String zurueck() { first -= noRows;
if (first < 0) first = 0; return null;
}
public String vor() {first += noRows;
if (first > size - noRows) first = size - noRows;
return null;
}
public String ende() {
first = size - noRows; return null;
}
Web-basierte Anwendungen
6. Juli 2015
251 / 295
Java Server Faces
Interaktion mit Kollektionen
Blättern – Mit dataTable
1
2
3
4
5
6
7
8
9
10
11
12
13
<h:commandLink action="#{dmbb.pager.start}" value="Start" /> &nbsp;
<h:commandLink action="#{dmbb.pager.zurueck}" value="Zurück" /> &nbsp;
<h:commandLink action="#{dmbb.pager.vor}" value="Vor" /> &nbsp;
<h:commandLink action="#{dmbb.pager.ende}" value="Ende" /> <br />
<h:outputText value="#{dmbb.pager.first} - / #{dmbb.pager.size}"/>
<h:dataTable var="name" value="#{dmbb.namesModel}"
first="#{dmbb.pager.first}" rows="#{dmbb.pager.noRows}">
<h:column>
<f:facet name="header">
<h:outputText value="Vorname" />
</f:facet>
<h:outputText value="#{name.vorname}" />
</h:column>
Umsetzung
• Binden der Links an die jeweils passende Methode
• Nutzen der Attribute first und rows von <h:datatable>
Allgemein
• Keine Logik im Facelet, nur Binden an Methoden (ohne Parameter)
• Nutzen der Features der UI-Komponenten (nicht Daten ändern)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
252 / 295
Java Server Faces
GET-Unterstützung
Bookmarks
Problem – POST
• JSF nutzt hauptsächlich POST
• GET für Bookmarks notwendig
• Bei GET kein JSF-Lebenstyklus
Lösung – Expliziter GET-Support
• Tags <h:link> und <h:button>
• Funktioniert außerhalb von <h:form>
• outcome-Attribut für Navigationsregel
• value-Attribut für Darstellung
• <f:param> (statt <f:attribute>) für
Parameterübergabe
• Nur in Ausnahmefällen verwenden
• JSF ist ein POST-Framework, hoch
interaktive Anwendungen (nicht nur
surfen)
ID für NameBean
DataModelBackingBean.java
1
2
3
4
5
6
7
8
9
10
Prof. Dr. Peter Barth (HS-RheinMain)
public String getId(NameBean nb) {
if (nb == null) { return "null"; }
String id = nb.vorname+"-"+nb.mittelname+"-"+
nb.nachname+"-"+nb.alter;
return id;
}
public String getIdCurrent() {
return getId(current);
}
public void setIdCurrent(String id) {
Web-basierte Anwendungen
6. Juli 2015
253 / 295
Java Server Faces
GET-Unterstützung
Bookmarks, GET-Unterstützung
selectnameget.xhtml
1
2
3
4
5
6
<h:column>
<h:link value="Details" outcome="detail">
<f:param name="dieid" value="#{dmbb.getId(name)}" />
</h:link>
</h:column>
</h:dataTable>
detailname.xhtml
1
2
3
4
5
6
<f:metadata>
<f:viewParam name="dieid" value="#{dmbb.idCurrent}" />
</f:metadata>
<h:panelGrid columns="2">
<h:outputLabel value="Vorname: " />
<h:outputText value="#{dmbb.currentCopy.vorname}" />
Umsetzung
• Ausgeben der ID, ausnahmsweise durch Funktion mit Argumenten
• Kopieren des Parameters dieid in die Property currentId mit setCurrentId,
geht jetzt nicht nur mit POST, sondern auch mit GET
Effekt: Bookmark-bar
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
254 / 295
Java Server Faces
GET-Unterstützung
Bookmarks – Anmerkungen
1
View-Parameter weitergeben
2
• Potenziell empfangen Seiten, die GET-Parameter
enthalten auch GET-Parameter
3
4
5
6
• Einfache Möglichkeit alle GET-Parameter
7
<h:column>
<h:link value="Details"
includeViewParams="true"
outcome="detail" >
...
</h:link>
</h:column>
weiterzugeben mit includeViewParams
• Verwendbar in <h:link> und <h:button>
Positionierung von <f:metadata>
• Darf nicht in einem Template oder einem
Seitenfragment (später) sein
• Kann umgangen werden mit Variablen, vermeiden
Verwenden von GET
• Nur für Bookmarks
• Nur bei Ansichten, nicht bei Änderungen
• Vermeiden, selten
• Web-Auftritt zum surfen mit JSP
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
255 / 295
Java Server Faces
Konvertierung und Validierung
Konvertierung und Validierung
Bisher
• Automatische Aktualisierung der Bean-Properties mit den Werten der
Eingabefelder
• Automatische Konvertierung für Standardtypen (Zahlen), ansonsten String
• Durch Konvertierung von Zahlen auch Validierung, dass nur Ziffern verwendet werden
Offen
• Direkte Konvertierung in benutzerspezifische Datentypen
Statt String-Property in der Backing-Bean direkt Property mit
benutzerspezifischem Typ
• Validierung der Eingabe nach benutzerspezifischen Kriterien
Umsetzung mit JSF
• Standardkonverter (automatisch) und benutzerspezifische Konverter
• Standardvalidierer (Tags, konfigurierbar) und benutzerspezifische Validierer
• Benutzerspezifische Konvertier und Validierer durch Klassen, die
JSF-Schnittstellen implementieren
• Konfiguration durch Annotation oder faces-config.xml
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
256 / 295
Java Server Faces
Konvertierung und Validierung
Konvertierung – Beispiel NameBean
Problemstellung
• Einzeiliges Eingabefeld für NameBean
• Eingabe besteht aus 4 durch
Leerzeichen getrennten Strings, die
den Komponenten zugeordnet werden
• Konvertierung von und zu namebean,
einer managed bean vom Typ
webanw.NameBean
• Konvertierungsfehler anzeigen:
Weniger/mehr als vier Elemente,
vierter Teil kein int
Ansicht
• Einzeilige Eingabe
• Einzelteile der konvertierten Bean
• Alles auf eine Seite, keine Navigation
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
257 / 295
Java Server Faces
Konvertierung und Validierung
Konverter – Einbettung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="nameeinzeilig" value="Name Einzeilig"/>
<h:inputText id="nameeinzeilig" value="#{namebean}"
converter="nameconverter" />
<h:message for="nameeinzeilig" />
<h:commandButton value="Update" />
</h:panelGrid>
<h:panelGrid columns="2">
<h:outputLabel value="Vorname:"/> <h:outputText value="#{namebean.vorname}" />
<h:outputLabel value="Mittelname:"/> <h:outputText value="#{namebean.mittelname}" />
<h:outputLabel value="Nachname:"/> <h:outputText value="#{namebean.nachname}" />
<h:outputLabel value="Alter:"/> <h:outputText value="#{namebean.alter}" />
</h:panelGrid>
</h:form>
• converter-Tag für Nutzung benutzerdefinierten
Konverter, Zeile 5
• <h:message for...> für Konverter-spezifische
Fehlermeldungen
• action nicht notwendig, gleiche Seite
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
258 / 295
Java Server Faces
Konvertierung und Validierung
Konverter – Implementierung
NameConverter.java
• FacesConverter-Tag zur
Registrierung unter Namen
nameconverter
• Alternativ in
faces-config.xml möglich
1
2
3
4
5
6
7
8
9
10
• getAsObject:
von String nach NameBean
• getAsString:
von NameBean nach String
• Konvertierungsfehler als
ConverterException
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Prof. Dr. Peter Barth (HS-RheinMain)
@FacesConverter("nameconverter")
public class NameConverter implements Converter {
public Object getAsObject(FacesContext fc,
UIComponent uic, String value) {
String[] parts = value.split("[\t\n\r ]+");
if (parts.length != 4) {
FacesMessage msg = new FacesMessage("keine 4 Teile");
throw new ConverterException(msg);
}
NameBean nb = new NameBean();
nb.setVorname(parts[0]);
nb.setMittelname(parts[1]);
nb.setNachname(parts[2]);
try {
nb.setAlter(Integer.parseInt(parts[3]));
} catch (NumberFormatException e) {
FacesMessage msg = new FacesMessage("4. Teil kein int");
throw new ConverterException(msg);
}
return nb;
}
public String getAsString(FacesContext fc,
UIComponent uic, Object value) {
if (value == null) { return null; }
NameBean nb = (NameBean) value;
return nb.getName() + " " + nb.getAlter();
}
}
28
Web-basierte Anwendungen
6. Juli 2015
259 / 295
Java Server Faces
Konvertierung und Validierung
Validierung
Manuell – Ohne spezifische Unterstützung
• action-Methode, bei Fehlschlag auf gleicher Seite bleiben
• Alles möglich, keine spezifische Unterstützung
• Ab und zu notwendig (komplexe, abhängige Validierung)
Implizit – Grundtypen und spezielle Attribute
• Properties bei Grundtypen wie Zahlen prüfen, ob passender Wert als String
vorliegt wie nur Ziffern
• Boolean, Byte, Character, Double, Float, Integer, Short, Long
• Attribute wie required="true"
Explizit – Ausdrücklich angegebene Validierer
• Angabe von Validierungsbedingungen in Standardvalidierer
zum Beispiel <f:validateLongRange mininum="0"maximum="128"/>
• Angabe von selbstgeschriebenen Validierer-Klassen oder -Methoden
Achtung: Validierer setzt erfolgreich konvertiertes Objekt voraus
• Validierer arbeitet auf konvertiertem Objekt, nicht auf String
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
260 / 295
Java Server Faces
Konvertierung und Validierung
Standard-Validatoren
1
Verwendung – <f:validate... />
2
3
• disabled-Attribut
4
• Weitere Attribute spezifisch je Tag
5
• Mehrere kombiniert erlaubt
7
6
8
Standard-Validatoren
9
10
• <f:validateLongRange>,
11
12
<f:validateDoubleRange>,
<f:validateLength>
13
(String)
<h:panelGrid columns="3">
<h:outputLabel for="zahl" value="Zahl" />
<h:inputText id="zahl" value="#{bb.zahl}">
<f:validateLongRange minimum="17" maximum="42" />
</h:inputText>
<h:message for="zahl" />
<h:outputLabel for="zk1" value="Zk1"/>
<h:inputText id="zk1" value="#{bb.zk1}">
<f:validateRegex pattern="a[bc]+d"/>
</h:inputText>
<h:message for="zk1" />
<h:commandButton value="Update" />
</h:panelGrid>
• minimum/maximum,
Zahl oder Anzahl Zeichen
• <f:validateRegex>, reg. Ausdruck
• <f:validateRequired>, Muß-Eingabe
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
261 / 295
Java Server Faces
Konvertierung und Validierung
Benutzerspezifische Validatoren – Beispiel
Anforderungen
Fehlschlag, bei 3 Ziffern
• Nur geradzahlige Zahlen
• Mit 3 Ziffern
• Oder festlegbarer Anzahl von Ziffern
• Passende Fehlermeldungen
In Ordnung
Fehlschlag, bei 2 Ziffern
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
262 / 295
Java Server Faces
Konvertierung und Validierung
Validator – Default Anzahl Ziffern
EvenDigitsValidator.java
Klasse EvenDigitsValidator
• Annotation @FacesValidator registriert
unter angegebene Namen den
Validator
public void validate(FacesContext fc,
UIComponent uic, Object value)
3
throws ValidatorException {
4
Integer zahl;
• Validator
validate
5
try {
6
zahl = (Integer) value;
• StateHolder
7
} catch (ClassCastException e) {
8
FacesMessage msg = new FacesMessage("not int");
validate
9
throw new ValidatorException(msg);
10
}
•
ValidatorException
11
if (zahl%2 != 0) {
12
FacesMessage msg = new FacesMessage("not even");
•
digits
13
throw new ValidatorException(msg);
14
}
Integer digits = Integer.valueOf(3);
15
zahl = zahl < 0 ? -zahl : zahl;
public void setDigits(Integer digits) {
16
if (zahl.toString().length() != digits) {
this.digits = digits;
17
String s =
}
18
String.format("not %d digits", digits);
19
FacesMessage msg = new FacesMessage(s);
20
throw new ValidatorException(msg);
21
}
22 }
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
263 / 295
• Auch mit <validator> unter
faces-config.xml
-Schnittstelle für
, kommt noch
Bei Fehlern
Instanzvariable
1
2
3
4
@FacesValidator(value="evendigits")
2 public class EvenDigitsValidator
3
implements Validator, StateHolder, Serializable {
1
, default 3
1
2
Java Server Faces
Konvertierung und Validierung
Facelet – Default Anzahl Ziffern
Facelet
• <f:validator>
• erlaubt direkte Nutzung eines
registrierten Validators
1
2
3
4
5
6
7
8
9
Prof. Dr. Peter Barth (HS-RheinMain)
<h:panelGrid columns="3">
<h:outputLabel for="ed"
value="Gerade Zahl mit 3 Ziffern" />
<h:inputText id="ed" value="#{bb.zahl}">
<f:validator validatorId="evendigits" />
</h:inputText>
<h:message for="ed" />
<h:commandButton value="Update" />
</h:panelGrid>
Web-basierte Anwendungen
6. Juli 2015
264 / 295
Java Server Faces
Konvertierung und Validierung
Validator – StateHolder
EvenDigitsValidator.java
StateHolder
1
• Wiederverwendung Validatoren
2
• Sinnvoll, spart Speicherallokation
4
• Durch Angabe Ziffern sind Instanzen
6
3
7
von EvenDigitsValidator
parametrisiert und damit
zustandsbehaftet
8
9
10
11
12
• sind StateHolder
13
14
Je nach Verwendung anders
parametrisiert
Im Beispiel:
• Zustand austauschbar
• Entwickler legt fest was und wie mit
saveState
5
boolean isTrans = false;
public void restoreState(FacesContext fc,
Object o) {
this.digits = (Integer) o;
}
public Object saveState(FacesContext fc) {
return this.digits;
}
public void setTransient(boolean b) {
this.isTrans = b;
}
public boolean isTransient() {
return this.isTrans;
}
• Zustand in Anzahl der Ziffern
• Default 3, kann aber gesetzt werden
und restoreState
• Es muss noch angegeben werden,
dass gespeichert werden darf
isTransient, Setter dann auch Zwang
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
265 / 295
Java Server Faces
Konvertierung und Validierung
Taglibs
web.xml
Flexible Validatoren nur als Taglib
• Falls Attribute, dann Deklaration
der Attribute notwendig
<context-param>
2
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
3
<param-value>/WEB-INF/mytags.taglib.xml</param-value>
4 </context-param>
1
mytags.taglib.xml
• Deklaration nur bei eigenem
Tag-Namen möglich
1
2
• Deklaration in Taglib notwendig
3
4
Taglib
• XML-Datei
• Sollte Endung .taglib.xml
haben
5
6
7
8
9
10
11
• Muss in web.xml deklariert
werden (oder in JARS unter
META-INF)
12
13
14
15
16
• Assoziiert Tag-Name mit
Validator-ID
17
18
<facelet-taglib
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_
version="2.2">
<namespace>myfaceletstags</namespace>
<tag>
<tag-name>validateEven</tag-name>
<validator>
<validator-id>evendigits</validator-id>
</validator>
<attribute>
<name>digits</name>
<required>true</required>
</attribute>
</tag>
</facelet-taglib>
• Deklariert Attribute
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
266 / 295
Java Server Faces
Konvertierung und Validierung
Validator – Setze Anzahl Ziffern
1
Taglib zur Verfügung stellen
2
3
• Als Namespace, Zeile 5
4
5
Neues Tag verwenden
6
• Dann intuitiv und einfach, Zeile 15
Fazit: Sehr aufwendig für Programmierer,
aber einfach für Verwender!
7
8
9
10
11
12
13
14
15
16
17
18
19
Prof. Dr. Peter Barth (HS-RheinMain)
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:my="myfaceletstags" >
<h:head>
<title>evendigits.xhtml</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="ed"
value="Gerade Zahl mit 2 Ziffern" />
<h:inputText id="ed" value="#{bb.zahl}">
<my:validateEven digits="2" />
</h:inputText>
<h:message for="ed" />
<h:commandButton value="Update" />
</h:panelGrid>
Web-basierte Anwendungen
6. Juli 2015
267 / 295
Java Server Faces
Konvertierung und Validierung
Bean-Validation – Ausblick
Problem
Beispiel-Annotationen:
• Validierung über Schichtgrenzen,
gleiche Einschränkungen bei UI-,
Anwendungs- und Persistenzschicht
• Mehrfache Implementierung:
• @NotNull: required
• @Min(value=0)
• @Max(value=128)
• @Pattern(regexp="a[bc]+d")
Mehraufwand, fehleranfällig,
Änderungen nicht synchron
• ...
• Benutzerdefinierte Constraints auch
Lösungsansatz – Bean-Validation
möglich
• Bean-Validation, JSR-303
• Meta-Datenmodell und Java-API zur
Bündelung von Constraints an Daten
• Validierungsregeln mit Annotationen
Integration – automatisch
• Aber leider noch nicht der volle
Funktionsumfang verfügbar
• Extension Validator schließt Lücken
Kleines Problem: @NotNull erzwingt Konfiguration
javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL auf true in
web.xml
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
268 / 295
Java Server Faces
Komposition und Templating
Komposition und Templating
Ziel – Modularer Seitenaufbau
• Zusammenfassung von Komponenten/Teilseiten zu Modulen
• Wiederverwendung von Seitenteilen und Komponenten
• Ohne komplexe Definition von Tag-Libraries
• Voraussetzung: Facelets, dadurch deklarative Beschreibung des UI, strukturiertes
XML (XHTML), Komponentenbaum
Lösung – Komposition und Templates, Facelets mit
<html ... xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
• Komposition
• Komponenten mit <ui:composition>, alles außerhalb des Tags ignorieren
• Zusammenbau mit <ui:include>, Seite aus Komponenten
• Parameter erlaubt
• Templating
• Templates (vollständiges Dokument) mit Platzhaltern <ui:insert>
• Verwenden mit <ui:composition> und ausfüllen mit <ui:define>
Mischen und Verschachteln beliebig möglich
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
269 / 295
Java Server Faces
Komposition und Templating
Komposition
header.xhtml
Komponenten/Module
1
2
• Vollständige XHTML-Dokumente
3
• Beinhaltet genau ein <ui:compostion>
4
5
• Alles außerhalb dieses
<ui:compostion>-Tags
copyright.xhtml
wird ignoriert
1
• Darin alle Tags erlaubt
2
3
Komposition
4
• Vollständiges XHTML-Dokument
5
• Mindestens ein <ui:include>-Tag
7
6
8
• Absoluter oder relativer Pfad
• Meist absolut unter /WEB-INF
• Inkludiert Komponenten/Module
<h:body>
Das wird ignoriert!
<ui:composition>
<div style="text-align: center">
Copyright 2015
</div>
</ui:composition>
</h:body>
page.xhtml
1
2
3
4
5
Prof. Dr. Peter Barth (HS-RheinMain)
<h:body>
<ui:composition>
<h3> Immer am Anfang der Seite </h3>
</ui:composition>
</h:body>
<h:body>
<ui:include src="header.xhtml" />
<p> Dazwischen. </p>
<ui:include src="copyright.xhtml" />
</h:body>
Web-basierte Anwendungen
6. Juli 2015
270 / 295
Java Server Faces
Komposition und Templating
Komposition – Parameter
headerparam.xhtml
Ziel
1
2
• Seitenteile parametrisieren
3
4
Parameter verwenden
5
<h:body>
<ui:composition>
<h3> Immer am Anfang von #{title} </h3>
</ui:composition>
</h:body>
• Innerhalb von <ui:composition>
pageparam.xhtml
• Einfach Parameternamen in
1
ExpressionLanguage-Ausdruck
verwenden
2
3
4
Parameter definieren
5
6
• Innerhalb von <ui:include>
7
<h:body>
<ui:include src="headerparam.xhtml">
<ui:param name="title" value="Super Titel"/>
</ui:include>
<p> Dazwischen. </p>
<ui:include src="copyright.xhtml" />
</h:body>
• <ui:param>-Tag mit name- und
value-Attribut
• Innerhalb von value kann beliebiger
Expression-Language Ausdruck
verwendet werden
• Damit auch Werte von Backing-Beans
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
271 / 295
Java Server Faces
Komposition und Templating
Templating
template.xhtml
Template Definition
1
• Vollständiges XHTML-Dokument mit
Platzhaltern
2
3
4
5
• <ui:insert>-Tags mit name-Attribut
6
7
• Default Inhalt ist Inhalt des Tags
8
9
Template Verwendung
10
11
• <ui:composition>-Tag
12
• template-Attribut
tpage.xhtml
• Innerhalb <ui:define>-Tags mit
name-Attribut,
<h:head>
<title><ui:insert name="title"/></title>
</h:head>
<h:body>
<h3> Immer am Anfang von
<ui:insert name="title">Titel</ui:insert>
</h3>
<p> <ui:insert name="content">Inhalt</ui:insert> </p
<div style="text-align: center">
Copyright 2015
</div>
</h:body>
1
ersetzen Platzhalter
2
3
4
5
6
7
8
9
10
<h:body>
<ui:composition template="template.xhtml">
<ui:define name="title">
Super Titel
</ui:define>
<ui:define name="content">
Dazwischen.
</ui:define>
</ui:composition>
</h:body>
Mischen (Komposition/Templating) erlaubt
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
272 / 295
Java Server Faces
Komposition und Templating
Templates in Templates
templateeins.xhtml
Teilweise ausgefüllte Templates sind 1
2
wieder Templates
<h:body>
<ui:composition template="template.xhtml">
3
<ui:define name="title">eins</ui:define>
4
</ui:composition>
5 </h:body>
templatezwei.xhtml
1
2
3
und können wieder ausgefüllt
werden
4
5
<h:body>
<ui:composition template="template.xhtml">
<ui:define name="title">zwei</ui:define>
</ui:composition>
</h:body>
templateeinsa.xhtml
1
2
3
4
5
<h:body>
<ui:composition template="templateeins.xhtml">
<ui:define name="content">A</ui:define>
</ui:composition>
</h:body>
templateeinsb.xhtml
<h:body>
2
<ui:composition template="templateeins.xhtml">
3
<ui:define name="content">B</ui:define>
4
</ui:composition>
5 </h:body>
Web-basierte
Anwendungen
6. Juli 2015
1
Prof. Dr. Peter Barth (HS-RheinMain)
273 / 295
Java Server Faces
Komposition und Templating
Mehrere Templates in einer Seite
linksrechtstemplate.xhtml
Ziel – Mehrere Templates in einer
Seite verwenden
Problem – Alles wegwerfen
• <ui:composition> klappt nicht,
es wird alles außerhalb
weggeworfen
1
2
3
4
5
6
7
8
9
Lösung – Neues Tag
• <ui:decorate>
• Verhalten wie <ui:composition>
• Aber umgebender Inhalt wird
nicht entfernt
linksrechtspage.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
Prof. Dr. Peter Barth (HS-RheinMain)
<h:body>
<ui:composition>
<table><tr><td>
<ui:insert name="links">links</ui:insert>
</td><td>
<ui:insert name="rechts">rechts</ui:insert>
</td></tr></table>
</ui:composition>
</h:body>
<h:body>
<ui:composition template="template.xhtml">
<ui:define name="title">Links Rechts Seite
</ui:define>
<ui:define name="content">
<ui:decorate template="linksrechtstemplate.xhtml">
<ui:define name="links">Linke Seite</ui:define>
<ui:define name="rechts">Rechte Seite</ui:define>
</ui:decorate>
</ui:define>
</ui:composition>
</h:body>
Web-basierte Anwendungen
6. Juli 2015
274 / 295
Java Server Faces
Komposition und Templating
Wiederholungen
<ui:repeat>
1
2
• Nur für die Ausgabe
3
• Ersatz für <c:forEach>
5
4
6
<ul>
<ui:repeat var="nb"
value="#{dmbb.liste}">
<li>#{nb.vorname} #{nb.nachname}</li>
</ui:repeat>
</ul>
• Funktioniert mit Facelets
• Verschachtelung erlaubt
Attribute
• value Iterierbare Kollektion
• var Laufvariable
• varStatus, offset, step
Bedingte Ausgabe
• Konditionale Ausdrücke oder
rendered-Attribut
Empfehlung: Vermeiden
• <h:dataTable> verwenden
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
275 / 295
Java Server Faces
Ressourcen-Management
Ressourcen-Management
Ressourcen
• Bilder, Skripte (JavaScript), Stylesheets (CSS)
• Meist direkte Einbindung über absoluten Pfad (innerhalb der Web-Anwendung)
Probleme mit Ressourcen
• Bei Komponentenbibliotheken will man nur ein JAR und nicht noch Verzeichnisse
kopieren
• Ort für die Festlegung muss je Seite in speziellem Bereich (Header) erfolgen
Lösung – Ressourcen-Management
• Generierung des HTML-Headers, <h:head> statt <head>
• Dadurch können Skript und Stylesheets an jeder Stelle verwendet werden, JSF
sammelt diese gibt alle notwendigen in <head> aus
• Neue Tags: <h:graphicImage>, <h:outputScript>, <h:outputStylesheet>
• Standardisierter Ressourcenzugriff: Integration in Expression Language,
Versionierung, Lokalisierung
• Für Komponentenentwickler @ResourceDependency-Annotation
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
276 / 295
Java Server Faces
Ressourcen-Management
Rahmen und Namen
1
Rahmen
2
3
• Statt <head> und <body> immer
<h:head>
4
und <h:body>
5
6
• JSF-Implementierung entscheidet was
7
8
zu <head> und <body> kommt
9
10
Neue Tags
11
• <h:graphicsImage>
12
13
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transiti
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transi
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Das HSRM-Logo</title>
</h:head>
<h:body>
<h:graphicImage name="hsrmlogo.png"/>
</h:body>
</html>
• Attribute name, Namen der Ressource
Namensauflösung
• JSF löst Namen auf, sucht Ressource
an mehreren Stellen
• /resources, in der Web-Anwendung
• In allen JAR-Dateien (und classpath)
unter /META-INF/resources
Beispiel: HSRM-Logo
Prof. Dr. Peter Barth (HS-RheinMain)
Pfad: resources/hsrmlogo.png
Web-basierte Anwendungen
6. Juli 2015
277 / 295
Java Server Faces
Ressourcen-Management
Bibliotheken, Expression Language
1
Bibliotheken
2
3
• Attribut library, Unterordner
4
5
• Expression-Language erlaubt
6
7
Expression Language
8
• resource-Objekt, existiert immer,
9
10
Pfadgenerierung
11
12
• value-Attribut für Pfadzugriff
13
14
• Geht dann auch im img-Tag
15
Sun-Logo, Library:
<h:graphicImage
library="images"
name="sunlogo.png"/>
<br />
Sun-Logo, Pfad:
<h:graphicImage
value="#{resource[’images:sunlogo.png’]}" />
<br />
Sun-Logo, img-Tag:
<img src="#{resource[’images:sunlogo.png’]}" />
<br />
Im Pfad
<h:outputText
value="#{resource[’images:sunlogo.png’]}" />
Beispiel: Sun-Logo
• Mit <h:graphicImage>, so verwenden
• Festlegung Bibliothek/Namen mit
Expression Language möglich
• Implizites resource-Objekt
• Direkte Pfadangabe, gib generierten
Pfad aus (nicht verwenden)
Prof. Dr. Peter Barth (HS-RheinMain)
/resources/images/sunlogo.png
Web-basierte Anwendungen
6. Juli 2015
278 / 295
Java Server Faces
Ressourcen-Management
Versionierung
1
Problem
2
3
• Aktualisierung von Ressourcen
4
5
• Teilweise, alte Anwendungsteile und
6
Inkompatibilitäten, noch alte Versionen 7
8
notwendig
9
10
• Mehrere Versionen müssen
<h:graphicImage library="images"
name="logo.png"/> <br />
<h:graphicImage library="images/2_0"
name="logo.png"/> <br />
<h:graphicImage library="images/2_1"
name="logo.png"/> <br />
<h:graphicImage library="images"
name="logo.png/1_0.png"/> <br />
<h:graphicImage library="images/2_0"
name="logo.png/1_0.png"/> <br />
koexistieren
Lösung – Versionierung
• Mit Unterstrich getrennte
Versionsnummer, Teil der Pfadangabe
• Bei Libraries und bei Ressourcen
• Bei Ressourcen wird Name zu Ordner
• Bei Auflösung ohne Versionsnummer
wird automatisch die höchste gewählt
• Explizite Angabe der Versionsnummer
erlaubt
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
279 / 295
Java Server Faces
Ressourcen-Management
Internationalisierung und Lokalisierung – Ressourcen
Bei Ressourcen
• javax.faces.resources.localePrefix
• Beim Suchpfad am Anfang mit
eingefügt
• Zusätzlich Versionierung zulässig
in Theorie, in Praxis Problem
• Ressource wird nicht gefunden
1
2
Fix:
3
4
• Manuell Locale einfügen als library
5
6
• Zugriff mit view.locale
7
8
• Setzen für Seite mit
9
<f:view locale="de">... </f:view>
10
11
12
Prof. Dr. Peter Barth (HS-RheinMain)
Locale:
<h:outputText value="#{view.locale}" /><br />
Richtig funktioniert nicht, de:
<h:graphicImage id="fehler"
name="daygreeting.png"/><br />
<h:message for="fehler"/><br />
Manuell geht, de:
<h:graphicImage library="#{view.locale.toString()}"
name="daygreeting.png"/><br />
Manuell geht, fr:
<h:graphicImage library="fr"
name="daygreeting.png"/>
Web-basierte Anwendungen
6. Juli 2015
280 / 295
Java Server Faces
Ressourcen-Management
Internationalisierung und Lokalisierung – Texte
faces-config.xml
Allgemein, JSF ähnlich zu JSP
1
2
• Basierend auf Locale
3
4
Verwenden
5
• Zugriff über expression language
6
• Je Seite: Bundle wählen mit
8
9
<f:loadBundle>
10
• Für ganze Anwendung:
<resource-bundle>
7
in
faces-config.xml
11
1
2
3
4
5
6
<application>
<locale-config>
<default-locale>de</default-locale>
<supported-locale>de</supported-locale>
<supported-locale>fr</supported-locale>
</locale-config>
<resource-bundle>
<base-name>webanw.texte</base-name>
<var>texte</var>
</resource-bundle>
</application>
Locale: <h:outputText value="#{view.locale}" />
<br />
Global: <h:outputText value="#{texte.hello}" />
<br />
<f:loadBundle basename="webanw.texte" var="ltexte"/>
Lokal: <h:outputText value="#{ltexte.hello}" />
<f:view locale="fr">
Locale: <h:outputText value="#{view.locale}" />
3 <br />
4 Hello: <h:outputText value="#{texte.hello}" />
5 </f:view>
1
2
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
281 / 295
Java Server Faces
Eigene Komponenten
Eigene Komponenten Definieren – composite
resources/components/mylist.xhtml
Ziel
• Einfache Realisierung von
Komponenten ohne Java-Code
1
2
3
4
5
• XHTML, keine weitere
Konfiguration
Lösung –Composites
• Neuer Namensraum, composite
6
7
8
9
10
11
12
• Spezieller Ordner
resources/components, keine
13
14
15
weitere Konfiguration notwendig 16
17
• Interface, Implementation
Beispiel
• Eigenes Tag mylist statt direkt
ui:repeat
18
19
20
21
22
23
24
• Klassen für Darstellung
gerade/ungerade als Attribute
Prof. Dr. Peter Barth (HS-RheinMain)
25
26
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:composite="http://xmlns.jcp.org/jsf/composite">
<h:head><title>MyList</title></h:head>
<h:body>
<composite:interface>
<composite:attribute name="value" />
<composite:attribute name="oddStyle" />
<composite:attribute name="evenStyle" />
</composite:interface>
<composite:implementation>
<ul>
<ui:repeat var="item" value="#{cc.attrs.value}"
varStatus="status">
<ui:fragment rendered="#{status.even}">
<li class="#{cc.attrs.evenStyle}"> #{item} </li>
</ui:fragment>
<ui:fragment rendered="#{status.odd}">
<li class="#{cc.attrs.oddStyle}"> #{item} </li>
</ui:fragment>
</ui:repeat>
</ul>
</composite:implementation>
</h:body>
</html>
27
Web-basierte Anwendungen
6. Juli 2015
282 / 295
Java Server Faces
Eigene Komponenten
Eigene Komponenten Verwenden – components
Bereitstellen
• UI-Library und Name des
Unterordners nach composite
1
2
3
4
5
6
• im Beispiel components
7
8
• Konvention ist gleicher Name im
Präfix
Stylesheet
9
10
11
12
13
• Unabhängige Ressource
14
15
• Einbindung wird automatisch im 16
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:components=
"http://xmlns.jcp.org/jsf/composite/components">
<h:head>
<title>mylist verwenden</title>
</h:head>
<h:body>
<h:outputStylesheet library="css" name="style.css" />
<components:mylist value="#{bb.liste}"
oddStyle="ungerade" evenStyle="gerade">
</components:mylist>
</h:body>
</html>
Header gerendert
/resources/css/style.css
1
2
3
4
5
6
.gerade {
background-color: green;
}
.ungerade {
background-color: red;
}
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
283 / 295
Java Server Faces
Ajax und Single Page
Ajax und Single Page Anwendungen
Problem – Seite Neu Laden
• Änderungen einer Web-Seite erfordert komplettes
Neugenerieren und Neuladen der Seite vom Server
• Unterschied bei vielen Seiten minimal
• Eingabe forciert Änderung in einem Nachbarfeld
• „Ticker“ ändert einen Wert kontinuierlich
Server
Ziel – Partieller Update
• Web-Seite bleibt (Single Page Anwendung), nur
Teile/Komponenten der Seite werden aktualisiert
• Interaktiver, reaktiver, weniger Datenverkehr
Umsetzung: Ajax, Asynchronuous JavaScript And XML
• Technische Grundlage: XMLHttpRequestObject
• Jesse James Garret prägt Begriff
Server
www.adaptivepath.com/ideas/essays/archives/000385.php
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
284 / 295
Java Server Faces
Ajax und Single Page
JavaScript
Historie – Einsatz im Browser
• 1995, Netscape Navigator, LiveScript/JavaScript
Ziel: Interaktivitiät, dynamisch Web-Seiten gestalten
• 1998, ECMA, Standardisierung
2000, Version 1.5, Basis-Featuresatz, können alle
2010, Version 1.9, ECMA-262, können fast alle
Sprachfeatures – OO-Skriptsprache
• Objektorientierte Skriptsprache, dynamische Typen,
funktional (innere Funktionen und Closures),
Prototypen-basiert, reguläre Ausdrücke, Dictionaries
eins
drei
zehn
• Klammern/Struktur C/Java, hat nichts mit Java zu tun
• Effiziente Implementierung in modernen Browsern
Dynamic HTML, DHTML
• Begriff Techniken interaktiver/animierter Web-Seiten
• DOM/CSS-Manipulation mit JavaScript
• Zu GUI-Apps aufschließen, initial Eye-Candy
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
285 / 295
Java Server Faces
Ajax und Single Page
Asynchroner Rückkanal – XMLHttpRequest
Historie
• 1999, IE 5.0, für Outlook Web Access
• 2000/2002 Mozilla
• 2005/2006 Standardisiert, Verfügbar (Desktop)
XMLHttp
Request
Ziel – Kanal zwischen Web-Browser und -Server
• Von Prog.sprache aus (JavaScript), asynchron
• Transport von Daten (nicht ganze Seite)
• XML als Datencontainer (heute JSON)
• Aktualisieren von Daten ohne Seite neu zu laden
API – www.w3.org/TR/XMLHttpRequest/
•
open(String method, String url, bool async);
• method = GET | PUT | HEAD
• url = URL für HTTP-Datenanfrage
• async = true, genau dann wenn asynchron
Server
• Zustandsbehaftet, Abfrage ob Download fertig
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
286 / 295
Java Server Faces
Ajax und Single Page
Ajax – Ergebnis
Ajax – Asynchronuous JavaScript and XML
• Asynchrones Laden von zu aktualisierenden Daten
• Benutzeroberfläche bleibt bedienbar
• Manipulation Anzeige, DHTML
42
XMLHttp
Request
• Partieller Seiten-Update
Ergebnis – Interaktive Web-Seite
• Rich, Look & Feel einer Desktop GUI-App
• Schnell, reagierend, nicht blockierend
Probleme – JavaScript
• JavaScript muss aktiviert sein
• Potenziell Performance-Fresser auf PC
• Kompatibilitätsprobleme, jede Client-Engine anders
Server 42
JavaScript Libraries – nicht mehr händisch
• Heutzutage meistens jQuery
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
287 / 295
Java Server Faces
Ajax und Single Page
Ajax und JSF
Einfach, fast perfekt seit JSF 2.0
• Ajax-Integration war wichtigstes Ziel bei JSF 2.0
• <f:ajax>, Deklaration der sich potenziell ändernden Komponenten bei partiellen
Seiten-Update
• Lebenszyklus angepasst für partielles Rendern, nur potenziell geänderte
Komponenten durchlaufen Lebenszyklus
• Native Unterstützung für partielles Seitenrendern, JavaScript-Library kommt mit
• Vollständiges Verstecken von JavaScript möglich!
• Durchgriff möglich
Vor JSF 2.0
• Riesenproblem mit Lebenszyklus, es musste immer ganze Seite gerendert werden
• Eigene inkompatible Erweiterungen der verschiedenen Anbieter:
MyFaces, IceFaces, PrimeFaces, . . .
• Alles sehr böse (aber InHouse-Apps laufen auch schnell und stabil mit kompletten
Seitenaufbau)
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
288 / 295
Java Server Faces
Ajax und Single Page
Beispiel – Single Page App mit Ajax
Beispiel – Ausgangssituation
Neuer Mittelname XXX, Ok(Ajax)
• Eingabe-Seite mit Ausgabebereich
Neuer Mittelname YYY, Ok(Seite)
• Ausgabe-Bereich geht von Name bis
Alter, aktuelles Datum separat
• Ok(Ajax): Datum (außerhalb Ausgabe)
bleibt, nicht neu gerendert
• Ok(Seite): Die ganze Seite, und damit
das Datum, wird aktualisiert
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
289 / 295
Java Server Faces
Ajax und Single Page
Beispiel – <f:ajax> Aufbau
Aufbau, JSF-Standard
• Alle Tags wie gewohnt
• Ein neues zusätzliches Tag
bei erstem Button,
„axafiziert“ Eingabe
<f:ajax>
<f:ajax>-Tag
• render: Mit id spezifierte Part
(Komponente) wird gerendert
• execute: Ausführen des mit id
spezifierten Parts, schicken
• Vorgabe ist @this, @form
umschließende Form, explizite
id möglich
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
• event: Wenn was passiert?
• Vorgabe action bei command*
oder onChange bei input*
• Alle on* nur ohne on
Prof. Dr. Peter Barth (HS-RheinMain)
23
<h:form>
<h:panelGrid columns="2" id="eingabe">
Vorname <h:inputText value="#{namebean.vorname}"/>
Mittelname <h:inputText value="#{namebean.mittelname}"/>
Nachname <h:inputText value="#{namebean.nachname}"/>
Alter
<h:inputText value="#{namebean.alter}"/>
</h:panelGrid>
<h:commandButton action="#{namebean.gueltig}"
value="Ok(Ajax)">
<f:ajax render="ausgabe"
execute="@form" event="action" />
</h:commandButton>
<h:commandButton action="#{namebean.gueltig}"
value="Ok(Seite)"/>
</h:form>
<h:panelGrid columns="2" id="ausgabe">
Hallo
<h:outputText value="#{namebean.name}"/>
Vorname <h:outputText value="#{namebean.vorname}"/>
Mittelname <h:outputText value="#{namebean.mittelname}"/>
Nachname <h:outputText value="#{namebean.nachname}"/>
Alter
<h:outputText value="#{namebean.alter}"/>
</h:panelGrid>
<center> #{bb.jetzt} </center>
Mehrere Teile bei render, durch Leerzeichen
getrennt
Web-basierte Anwendungen
6. Juli 2015
290 / 295
Java Server Faces
Ajax und Single Page
Beispiel – <f:ajax> Ablauf
Client, Web-Browser
• Benutzer klickt „Ok(Ajax)“
1
2
3
4
• Deklaratives Ajax-Tag
5
• Klick löst JavaScript aus, kein
7
Neu-Laden der Seite
• Wege execute="@form" wird
6
8
9
10
11
alles im ersten panelGrid (Form) 12
13
zum Server geschickt
14
Server
• Lebenszyklus läuft für
Komponenten der Form
15
16
17
18
19
20
• Aktualisieren der Beans
• Nicht rendern, aber Daten für
rendering des ausgabe-Bereichs
extrahieren und zum Client
schicken
Prof. Dr. Peter Barth (HS-RheinMain)
21
22
23
<h:form>
<h:panelGrid columns="2" id="eingabe">
Vorname <h:inputText value="#{namebean.vorname}"/>
Mittelname <h:inputText value="#{namebean.mittelname}"/>
Nachname <h:inputText value="#{namebean.nachname}"/>
Alter
<h:inputText value="#{namebean.alter}"/>
</h:panelGrid>
<h:commandButton action="#{namebean.gueltig}"
value="Ok(Ajax)">
<f:ajax render="ausgabe"
execute="@form" event="action" />
</h:commandButton>
<h:commandButton action="#{namebean.gueltig}"
value="Ok(Seite)"/>
</h:form>
<h:panelGrid columns="2" id="ausgabe">
Hallo
<h:outputText value="#{namebean.name}"/>
Vorname <h:outputText value="#{namebean.vorname}"/>
Mittelname <h:outputText value="#{namebean.mittelname}"/>
Nachname <h:outputText value="#{namebean.nachname}"/>
Alter
<h:outputText value="#{namebean.alter}"/>
</h:panelGrid>
<center> #{bb.jetzt} </center>
Client: Empfang Daten, Aktualisieren DOM des
ausgabe Bereichs
Web-basierte Anwendungen
6. Juli 2015
291 / 295
Java Server Faces
Ajax und Single Page
Beispiel – Klappen
• Rechts: Zk1 Ok(Ajax),
Zk1 Update(Ajax), Zk2 Update(Seite)
• Unten: Nicht Zk2 zeigen
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
292 / 295
Java Server Faces
Ajax und Single Page
Beispiel – Klappen, Detail
boxes
Blendet Zeilen in
panels ein und aus
• Welche Zeilen angezeigen?
• Boolesche Property je String-Property
panels
• Label, Eingabefeld, anzeigen?
boxes
• Je Zeichenkette eine Zeile
• Zeilen können ausgeblendet werden
panels
zeit:
Aktuelle Uhrzeit aus Backing-Bean
Sonst
• Ok(Ajax), aktualisieren ohne
zeit
Eingabewerte zu lesen
• Update(Ajax), aktualisieren mit
Eingabefelder
Eingabewerte lesen
• Update(Seite), ganzer Request
Aktuelle Zeit
• Nochmal aktuelle Uhrzeit, ohne Ajax
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
293 / 295
Java Server Faces
Ajax und Single Page
Beispiel – Klappen, Umsetzung
Umschließendes <f:ajax>
• Default-Verhalten für alle
umschlossenen Elemente
• Checkbox mit onChange
• sonst nichts
• Bei Änderung neu rendern
1
2
3
4
5
6
7
8
9
10
11
• Effekt: Klick klappt Zeile ein oder12
aus
13
14
Unteres <f:ajax>
• Wie vorher
...
1
2
• Explizit eine der zwei
Zeitangaben neu rendern
Anmerkungen:
• rendered und id nicht mischbar
3
4
5
6
7
8
9
10
Prof. Dr. Peter Barth (HS-RheinMain)
<h:form>
<f:ajax render="panels">
<h:panelGrid columns="3" id="boxes">
Zk1 <h:outputText value="Zk2"/> Zk3
<h:selectBooleanCheckbox value="#{bb.showZk1}" />
<h:selectBooleanCheckbox value="#{bb.showZk2}" />
<h:selectBooleanCheckbox value="#{bb.showZk3}" />
</h:panelGrid>
</f:ajax>
<h:panelGrid columns="1" id="panels">
<h:panelGrid columns="3" border="1"
rendered="#{bb.showZk1}">
Zk1 <h:inputText value="#{bb.zk1}"/> #{bb.showZk1}
</h:panelGrid>
<h:commandButton value="Ok(Ajax)">
<f:ajax render="panels zeit" execute="boxes"/>
</h:commandButton>
<h:commandButton value="Update(Ajax)">
<f:ajax render="panels zeit" execute="boxes panels"/>
</h:commandButton>
<h:commandButton value="Update(Seite)" />
<h:outputText value="#{bb.jetzt}" id="zeit" />
</h:form>
<center>#{bb.jetzt}</center>
Web-basierte Anwendungen
6. Juli 2015
294 / 295
Java Server Faces
Wie Weiter
Wie Weiter . . .
JavaEE – mehr als nur Web-UI
• Anwendungs- und Datenschicht,
im Master oder in Vertiefungen
• Persistenzlayer (JPA),
im Projekt gegeben
• Enterprise Java Beans,
Application Server
Alternative Web-Frameworks
• Java: Struts, Vaadin, . . .
• JavaVM: Grails (Groovy), . . .
• Andere Sprachen/Umgebungen
• Ruby on Rails
• Django (Python),
Sommerschool bei Interesse!
• Zend (PHP)
• Andere UI-Ansätze: GWT, JQuery/ExtJS, . . .
Prof. Dr. Peter Barth (HS-RheinMain)
Web-basierte Anwendungen
6. Juli 2015
295 / 295