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 /> <tag> <br /> <b> bold </b> <br /> a " 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 © <%= 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 © <%= 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 /> <c:out><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}" /> </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}" /> <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 © <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 © 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 © <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" /> <h:commandLink action="#{dmbb.pager.zurueck}" value="Zurück" /> <h:commandLink action="#{dmbb.pager.vor}" value="Vor" /> <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
© Copyright 2024 ExpyDoc