JavaServer Faces: JSF verstehen und praktisch einsetzen [3. Aufl.] 9783658318024, 9783658318031

Das Buch setzt absichtlich nicht auf eine formale Beschreibung; stattdessen wird Wissen auf eine lebendige und abwechslu

632 71 5MB

German Pages XI, 270 [269] Year 2020

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

JavaServer Faces: JSF verstehen und praktisch einsetzen [3. Aufl.]
 9783658318024, 9783658318031

Table of contents :
Front Matter ....Pages i-xi
Aufbau und Gliederung * (Michael Goll)....Pages 1-4
Der Schnelleinstieg * (Michael Goll)....Pages 5-7
Grundlagen der Webentwicklung * (Michael Goll)....Pages 9-14
Java-Webentwicklung mit Servlets und JSPSeiten * (Michael Goll)....Pages 15-25
JavaServer Faces * (Michael Goll)....Pages 27-58
Fallstudie Blog-Anwendung – Übersicht * (Michael Goll)....Pages 59-62
Grundlegende JSF-Komponenten * (Michael Goll)....Pages 63-79
Templating * (Michael Goll)....Pages 81-84
Internationalisierung * (Michael Goll)....Pages 85-90
Navigation * (Michael Goll)....Pages 91-97
Konverter * (Michael Goll)....Pages 99-107
Validierung * (Michael Goll)....Pages 109-129
Listener * (Michael Goll)....Pages 131-134
Erweiterte Komponenten * (Michael Goll)....Pages 135-159
Komponentenbaum *** (Michael Goll)....Pages 161-164
Der JSF-Lebenszyklus *** (Michael Goll)....Pages 165-174
Rund um die JSF-Anwendung *** (Michael Goll)....Pages 175-178
Konfigurationsdateien * (Michael Goll)....Pages 179-182
Verwaltung von Ressourcen * (Michael Goll)....Pages 183-188
Asynchronous JavaScript and XML ** (Michael Goll)....Pages 189-196
HTML5 Friendly Markup *** (Michael Goll)....Pages 197-200
WebSocket *** (Michael Goll)....Pages 201-208
JSON Verarbeitung *** (Michael Goll)....Pages 209-213
Wiederverwendung *** (Michael Goll)....Pages 215-235
Faces Flows *** (Michael Goll)....Pages 237-247
JSF-Kern erweitern *** (Michael Goll)....Pages 249-254
Ausblick ** (Michael Goll)....Pages 255-255
Anhang A Eclipse * (Michael Goll)....Pages 257-264
Back Matter ....Pages 265-270

Citation preview

Michael Goll

JavaServer Faces JSF verstehen und praktisch einsetzen 3. Auflage

JavaServer Faces

Michael Goll

JavaServer Faces JSF verstehen und praktisch einsetzen 3., überarbeitete Auflage

Michael Goll Solingen, Deutschland

ISBN 978-3-658-31802-4 ISBN 978-3-658-31803-1  (eBook) https://doi.org/10.1007/978-3-658-31803-1 Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detail­ lierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar. © Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2010, 2019, 2020 Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Jede Verwertung, die nicht ausdrücklich vom Urheberrechtsgesetz zugelassen ist, bedarf der vorherigen Zustimmung des Verlags. Das gilt insbesondere für Vervielfältigungen, Bearbeitungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen. Die Wiedergabe von allgemein beschreibenden Bezeichnungen, Marken, Unternehmensnamen etc. in diesem Werk bedeutet nicht, dass diese frei durch jedermann benutzt werden dürfen. Die Berechtigung zur Benutzung unterliegt, auch ohne gesonderten Hinweis hierzu, den Regeln des Markenrechts. Die Rechte des jeweiligen Zeicheninhabers sind zu beachten. Der Verlag, die Autoren und die Herausgeber gehen davon aus, dass die Angaben und Informationen in diesem Werk zum Zeitpunkt der Veröffentlichung vollständig und korrekt sind. Weder der Verlag, noch die Autoren oder die Herausgeber übernehmen, ausdrücklich oder implizit, Gewähr für den Inhalt des Werkes, etwaige Fehler oder Äußerungen. Der Verlag bleibt im Hinblick auf geografische Zuordnungen und Gebietsbezeichnungen in veröffentlichten Karten und Institutionsadressen neutral. Planung: Sybille Thelen Marcel Urbanek war Autor der ersten Auflage und Co-Autor der zweiten Auflage des Buches. Springer Vieweg ist ein Imprint der eingetragenen Gesellschaft Springer Fachmedien Wiesbaden GmbH und ist ein Teil von Springer Nature. Die Anschrift der Gesellschaft ist: Abraham-Lincoln-Str. 46, 65189 Wiesbaden, Germany

Vorwort zur 3. Auflage Dieses Buch ermöglicht den schnellen Einstieg in die JSF-Technologie ( JavaServer

Das Buch

Faces). Es erhebt dabei nicht den Anspruch auf absolute Vollständigkeit, sondern versucht dem Leser in Kürze die wesentlichen Bestandteile der JSF-Technologie näher zu bringen. Um den Einstieg so einfach wie möglich zu gestalten, werden möglichst einfache Formulierungen und zahlreiche Grafiken verwendet: Ziel war es nicht, eine formal korrekte Beschreibung der Technologie zu erstellen. Ziel war es, lebendig und abwechslungsreich Wissen zu vermitteln. Der Autor beschäftigt sich seit längerer Zeit beruflich mit dem Thema Programmierung von Webanwendungen, speziell JSF und ASP.NET, aber auch PHP ( PHP Hypertext Pre-

Der Autor

processor) und JSP ( JavaServer Pages). Neben diesen Technologien beschäftigt sich der Autor ebenfalls mit anderen Technologien wie JavaScript und zahlreichen weiteren Frameworks. Dieses Lehrbuch verwendet eine neue Didaktik.

Neue Didaktik

Der Buchaufbau und die didaktischen Elemente sind im Anschluss an dieses Vorwort beschrieben (Hinweise des Verlags). In der Ihnen vorliegenden dritten Auflage werden durchgehend CDI-Managed Beans verwendet. Die Verwendung von JSF-Managed Beans wurde als deprecated gekenn-

Neu in der 3. Auflage

zeichnet. Ferner werden die Themen Komponenten-Validierung und WebSocket -Integration aufgegriffen. Neben diesem Themenauszug wird zusätzlich bei den JSF-Beispielen durchgehend Facelets als Seitendeklarationssprache eingesetzt. Eine Fallstudie »Blog -Anwendung« wird ausgehend von einem sehr einfachen Beispiel sukzessive um verschiedene Themen wie Internationalisierung, Konvertierung usw. ergänzt und abschließend in einer Gesamtlösung vereint. Ich widme dieses Buch in besonderem Maße Marla. Ganz herzlich möchte ich mich bei meinen Eltern, Susanne, Natalie und Robert für ihre starke moralische Unterstützung

Danksagung

und ihr Verständnis danken. Und nun wünsche ich Ihnen viel Spaß und Erfolg beim Lesen dieses Buches, Ihr Michael Goll

An den Start

vi

Vorwort zur 3. Auflage

Hinweise des Verlags Zum Aufbau des Buches

Dieses Buch besteht aus Kapiteln und Unterkapiteln. Jedes Unterkapitel ist im Zeitungsstil geschrieben. Am Anfang steht die Essenz, d. h. das Wesentliche. Es kann Ihnen zur Orientierung dienen – aber auch zur Wiederholung. Anschließend kommen die Details. Die Essenz ist grau hervorgehoben.

Fallstudie

Durch Fallstudien wenden Sie das Erlernte auf umfangreiche Problemstellungen aus der Praxis an.

Box Sternesystem

Boxen fassen Wichtiges zum Nachschlagen zusammen. Jedes Kapitel und Unterkapitel ist nach einem Sternesystem gekennzeichnet: * = Grundlagenwissen ** = Vertiefungswissen *** = Spezialwissen **** = Expertenwissen Dieses Sternesystem hilft Ihnen, sich am Anfang auf die wesentlichen Inhalte zu konzentrieren (1 und 2 Sterne) und sich vielleicht erst später mit speziellen Themen (3 und 4 Sterne) zu befassen. Übungen ermöglichen eine Selbstkontrolle und Vertiefung des Stoffes. Sie sind durch ein Piktogramm in der Marginalspalte gekennzeichnet. Tests einschließlich automatischer Korrekturen finden Sie in dem zugehörigen (kostenpflichtigen) E-Learning-Zertifikatskurs.

Beispiel

Beispiele helfen Sachverhalte zu verdeutlichen. Sie sind in der Marginalspalte mit »Beispiel« gekennzeichnet. Der Beispieltext ist mit einem Grauraster unterlegt.

Tipps/ Hinweise

Definitionen Glossar

Hilfreiche Tipps, Empfehlungen und Hinweise sind durch eine graue Linie vom restlichen Text getrennt. Definitionen werden durch graue, senkrechte Balken hervorgehoben. Glossarbegriffe sind fett gesetzt, wichtige Begriffe grau hervorgehoben. Ein vollständiges Glossarverzeichnis finden Sie am Buchende. Dieses Piktogramm zeigt an, dass wichtige Inhalte nochmals in einer so genannten Merkebox zusammengefasst wiederholt werden – oft unter einer anderen Perspektive, um den Lerneffekt zu erhöhen. Dieses Piktogramm zeigt an, dass der Text Verweise zum E-Learning-Kurs dieses Buches enthält.

Frage & Antwort

In den meisten Lehrbüchern wird »die Welt« so erklärt, wie sie ist – ohne dem Leser vorher die Möglichkeit gegeben zu haben, über »die Welt« nachzudenken. In einigen Kapiteln werden Ihnen Fragen gestellt. Diese Fragen sollen Sie dazu anregen, über ein Thema nachzudenken. Erst nach dem Nachdenken sollten Sie weiter lesen. (Vielleicht sollten Sie die Antwort nach der Frage zunächst durch ein Papier abdecken).

vii

Vorwort zur 3. Auflage

Für viele Begriffe – insbesondere in Spezialgebieten – gibt es keine oder noch keine geeigneten oder üblichen deutschen Begriffe. Gibt es noch keinen eingebürgerten

Englische Begriffe kursiv

deutschen Begriff, dann wird der englische Originalbegriff verwendet. Englische Bezeichnungen sind immer kursiv gesetzt, sodass sie sofort ins Auge fallen. Damit Sie referenzierte Seiten schnell finden, enthalten alle Querverweise absolute Seitenzahlen.

Querverweise

Inhalt 1

Aufbau und Gliederung * .........................................................

1

2

Der Schnelleinstieg *.................................................................

5

3

Grundlagen der Webentwicklung *......................................... Java Specification Request **....................................................... Architektur von Webanwendungen * ...........................................

9

3.1 3.2 4 4.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.4

Java-Webentwicklung mit Servlets und JSP-Seiten *............. Servlets * ..................................................................................... JSP-Seiten *.................................................................................. JSP-Skripting * ............................................................................. JSP Standard Tag Library * ........................................................... Expression Language *................................................................. JSF-Seiten *.................................................................................. Fazit zu Servlets und JSP-Seiten * .................................................

9 10 15 16 18 18 19 21 23 25

5.8

JavaServer Faces *..................................................................... Historie * ..................................................................................... Architektur von JSF *.................................................................... View Declaration Language *....................................................... Tag -Bibliotheken * ....................................................................... Expression Language *................................................................. Managed Beans * ........................................................................ Einführung in Managed Beans * .................................................. Contexts and Dependency Injection * .......................................... Contexts * ................................................................................... Dependency Injection *................................................................ Weiterführende Konfiguration **................................................. Annotationen vs. Konfigurationsdatei **...................................... Bindings **..................................................................................

6

Fallstudie Blog -Anwendung – Übersicht * ..............................

59

7

Grundlegende JSF-Komponenten * .........................................

63

7.1

Box: Komponentenhierarchie * ....................................................

64

7.2

h:outputText *................................................................................

66

7.3

h:inputText, h:inputSecret, h:inputTextarea * .......................................

68

7.4

h:commandButton, h:commandLink * ................................................

72

7.5

Die Komponenten im Zusammenspiel *........................................

75

7.6

Fallstudie Blog -Anwendung – Eingabemaske *.............................

76 81

5 5.1 5.2 5.3 5.4 5.5 5.6 5.6.1 5.6.2 5.6.3 5.6.4 5.6.5 5.7

27 27 29 30 31 33 35 36 37 39 44 48 54 56

8

Templating * ..............................................................................

8.1

JSF-Templating *..........................................................................

81

8.2

Fallstudie Blog -Anwendung – Templating * .................................

83

x

Inhalt

9

Internationalisierung * .............................................................

85

9.1

JSF-Internationalisierung ** ..........................................................

85

9.2

Fallstudie Blog -Anwendung – Internationalisierung ** .................

89

10

Navigation * ..............................................................................

91

10.1

JSF-Navigation **.........................................................................

91

10.2

Fallstudie Blog -Anwendung – Navigation ** ................................

96

11

Konverter * ................................................................................

99

11.1

JSF-Konverter ** ..........................................................................

99

11.2

Eigene Konverter ***................................................................... 102

11.3

Fallstudie Blog -Anwendung – Konverter ***................................ 106

12

Validierung *.............................................................................. 109

12.1

JSF-Validierung *.......................................................................... 109

12.2

Darstellen von Validierungsfehlern **........................................... 113

12.3

Aussetzen der Validierung ** ....................................................... 117

12.4

Eigene Validierung *** ................................................................ 120 Bean Validation ***..................................................................... 124 Fallstudie Blog -Anwendung – Validierung *** ............................. 128

12.5 12.6

13.1

Listener * ................................................................................... 131 Action Listener ** ........................................................................ 131

13.2

Value Change Listener ** ............................................................ 133

14

Erweiterte Komponenten * ...................................................... 135

14.1

Auswahllisten *............................................................................ 135

13

14.1.1

JSF-Auswahllisten * ...................................................................... 135

14.1.2

Fallstudie Blog -Anwendung – Auswahllisten *.............................. 142

14.2

Dateiupload * .............................................................................. 143

14.2.1

JSF-Dateiupload *......................................................................... 144

14.2.2

Fallstudie Blog -Anwendung – Dateiupload * ................................ 146

14.3

Erweiterte Komponenten – Tabellen * .......................................... 148

14.3.1

JSF-Tabellen ** ............................................................................ 148

14.3.2

Tabellen – das Master Detail Pattern ***...................................... 154

14.3.3

Fallstudie Blog -Anwendung – Tabelle ** ...................................... 157

15

Komponentenbaum *** ........................................................... 161

16

Der JSF-Lebenszyklus *** ......................................................... 165

17

Rund um die JSF-Anwendung *** ........................................... 175

18

Konfigurationsdateien * ........................................................... 179

18.1

web.xml * .................................................................................... 179

18.2

faces-config.xml * ........................................................................ 180

19

Verwaltung von Ressourcen * .................................................. 183

19.1

JSF-Ressourcen ** ........................................................................ 183

Inhalt

19.2

Fallstudie Blog -Anwendung – Ressourcen **................................ 187

20

Asynchronous JavaScript and XML **..................................... 189

21

HTML5 Friendly Markup *** .................................................... 197

22

WebSocket ***.......................................................................... 201

23

JSON Verarbeitung *** ............................................................. 209

24

Wiederverwendung *** ........................................................... 215

24.1

Eigene Komponenten ***............................................................ 218

24.2 24.3

Eigene Renderer ***.................................................................... 223 Eigene Tags ***........................................................................... 225

24.4

Composite Components *** ....................................................... 230

25

Faces Flows *** ......................................................................... 237

26

JSF-Kern erweitern *** ............................................................. 249

27

Ausblick **................................................................................. 255

Anhang A Eclipse *...................................................................................... 257 Literatur .......................................................................................................... 265 Sachindex ....................................................................................................... 266

xi

1

Aufbau und Gliederung *

Die Technik JSF ( JavaServer Faces) ist ein serverseitiges Framework und wird zur Erstellung von Webanwendungen eingesetzt. JSF versucht die Komplexität der Webanwendungsentwicklung zu kapseln und verbirgt einen großen Teil der Probleme und Techniken, mit denen sich ein Webentwickler beschäftigen muss. So müssen die meisten Entwickler des Projektteams nur ein rudimentäres Wissen über Webanwendungen besitzen, da sie hauptsächlich in ihrer gewohnten Programmiersprache entwickeln können und sich nur selten vergegenwärtigen müssen, dass sie eine Webanwendung entwickeln. Vollständig kann auch JSF den Entwickler nicht vor dem Kontakt mit Webtechniken abschirmen. Nach der Lektüre dieses Buchs werden Sie einfache JSF-Anwendungen entwickeln kön-

Ziel

nen. Da auch die internen Abläufe und Konzepte des JSF-Frameworks beleuchtet werden, sollten Sie zudem JSF geeignet erweitern und Probleme, die bei der Entwicklung mit JSF auftreten, lösen können. Schon nach den ersten Kapiteln werden Sie zumindest in der Lage sein, eine einfache JSF-Anwendung zu realisieren und JSF als Technik einordnen zu können. Nach dem letzten Kapitel werden Sie auch dazu in der Lage sein, selbständig JSF-Anwendungen umzusetzen. Dazu wird immer wieder auf praktische Übungen hingewiesen sowie Beispiele und Problemstellungen aus der Praxis aufgezeigt, die Ihnen im alltäglichen Umgang mit JSF

Vorgehensweise

vermutlich begegnen werden. Aufgabe des Buchs ist, Sie auf die Praxis vorzubereiten, ohne die Theorie zu kurz kommen zu lassen. Nach den ersten praktischen Beispielen werden die eher theoretischen Grundlagen von JSF erklärt. Dies jedoch sehr lebendig an den vorher besprochenen Beispielen, sodass Sie keine Probleme haben werden, den Ausführungen zu folgen. Beachten Sie dabei bitte, dass die gezeigten Beispiele an geeigneten Stellen gekürzt sind, um nur die wesentlichen Aspekte zu verdeutlichen. Soweit also zu dem, was das Buch Ihnen bietet. Jetzt aber zu dem, was das Buch von Ihnen erwartet:  

Das Durcharbeiten der praktischen Übungen. Java-Kenntnisse (z. B. [Balz13], [Balz14])



Grundlegende Kenntnisse der Entwicklung von Webanwendungen unter Java (z. B. [KrBa11], [Wißm09]):



Sie wissen, wie man mit der von Ihnen bevorzugten Entwicklungsumgebung ein



Webanwendungsprojekt erstellt. Sie haben schon einmal eine JSP-Anwendung ( JavaServer Pages) selber geschrie-

Was von Ihnen erwartet wird

ben. 

Sie haben folgende Begriffe schon einmal gehört und können sie grob einordnen:

e Markup -Element (Tag ) e Tag -Bibliothek (Tag Library ) e Servlet

Sollten Sie nicht alle Kriterien erfüllen, ist das kein Ausschlusskriterium. Sie können erst einmal versuchen, sich »durchzuschlagen«. An den entsprechenden Stellen des Buchs werden alle Begriffe noch einmal kurz erläutert. Da alle Begriffe in einen praktischen Kontext eingebunden sind, sehen Sie direkt, wie diese Techniken zu verwenden sind. Sollte das nicht ausreichen, können Sie das eine oder andere im Internet nachlesen.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_1

Wenn nicht alles zutrifft

2

1 Aufbau und Gliederung *

Falls Sie eher die Buchform bevorzugen, ist das Buch [Wißm09] (mit E-Learning-Kurs) zu empfehlen, das Ihnen alle Grundlagen vermittelt, die für das Verständnis dieses Buches und der JSF-Technik nötig sind. Entwicklungsumgebung

Im Vergleich zur Programmierung mit einer Programmiersprache werden bei Weban-

Schnelleinstieg

Ziel des Schnelleinstiegs ist es, Ihnen ein Gefühl für die JSF-Technik zu vermitteln. Dazu

wendungen mehrere Werkzeuge genutzt. Sie sollten sich die notwendigen Werkzeuge auf Ihrem Computersystem installieren und konfigurieren.

dient ein einfaches Beispiel als Motivation.  Grundlagen Webentwicklung

»Der Schnelleinstieg«, S. 5

JSF ist eine Spezifikation und es existieren mehrere Implementierungen dieser Spezifikation. Der Weg der Spezifikation zur Implementierung wird in den Grundlagen gezeigt und es werden allgemeine Architekturen von Webanwendungen beschrieben. 

Servlets & JSPSeiten

Servlets und JSP ( JavaServer Pages) sind vor der Einführung von JSF genutzt worden. Die vorhandenen Techniken werden verglichen. 

JSF

»Grundlagen der Webentwicklung«, S. 9

»Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15

Die Historie von JSF ermöglicht ein grundlegendes Verständnis für die aktuell vorhandene Version. JSF wird in den Java-Kontext eingeordnet und die grundlegenden Themen wie View Declaration Language, Expression Language und Managed Beans werden erläutert. 

Fallstudie

»JavaServer Faces«, S. 27

Zur Verdeutlichung der jeweiligen Themen wird eine Fallstudie beschrieben, die durch die jeweils präsentierten Konzepte erweitert wird. Zunächst wird ein Überblick über die Fallstudie gegeben, die im weiteren Verlauf sukzessive erweitert wird. 

JSFKomponenten

Einige grundlegende Komponenten wie h:outputText, h:inputText oder h:commandButton erlauben bereits an der Stelle kleinere JSF-Anwendungen zu realisieren. 

Templating

»Fallstudie Blog-Anwendung – Übersicht«, S. 59

»Grundlegende JSF-Komponenten«, S. 63

Es können Vorlagen erstellt werden, die von mehreren Seiten verwendet werden können. Dies vermeidet Redundanzen und reduziert Fehler. 

Internationalisierung

»Templating«, S. 81

Oft muss eine Webanwendung in verschiedenen Sprachen angeboten werden. JSF bietet Möglichkeiten, um eine Sprache austauschbar zu halten. 

»Internationalisierung«, S. 85

3

1 Aufbau und Gliederung *

Üblicherweise besteht eine Webanwendung nicht nur aus einer Seite. Die Navigation zwischen den Seiten kann mit JSF konfiguriert werden. 

Navigation

»Navigation«, S. 91

Benutzereingaben werden über den Webbrowser als textuelle Darstellung übertragen.

Konverter

Diese Eingaben werden von JSF in das benötigte Format konvertiert, beispielsweise eine Zahl. 

»Konverter«, S. 99

Die vom Benutzer eingegebenen Daten müssen validiert werden. Es können verschie-

Validierung

dene Validierungsmechanismen eingesetzt werden, z. B. Validierung bezüglich der Textlänge oder einer Zahlendarstellung. 

»Validierung«, S. 109

Mit JSF können Listener eingesetzt werden, die festgelegte Programmteile aufrufen,

Listener

wenn bestimmte Ereignisse eintreten. 

»Listener«, S. 131

Dieser Buchteil vermittelt ein tieferes Verständnis von JSF. Dazu zählen komplexere Eingabekomponenten wie Auswahllisten und Tabellen, die aus mehr als nur einem

Erweiterte Komponenten

Tag bestehen, aber auch das Hochladen von Dateien. 

»Erweiterte Komponenten«, S. 135

Die verwendeten Elemente innerhalb einer JSF-Seite werden in einem Komponentenbaum abgebildet und anschließend dargestellt. 

Komponentenbaum

»Komponentenbaum«, S. 161

Es ist wichtig den JSF-Lebenszyklus zu verstehen, um Einschränkungen von JSF zu er-

JSF-Lebenszyklus

kennen und auftretende Fehler beheben zu können. 

»Der JSF-Lebenszyklus«, S. 165

Mittels spezieller Klassen kann mit dem JSF-Framework und darüber hinaus kommuniziert werden. 

JSF-Umgebung

»Rund um die JSF-Anwendung«, S. 175

Das Verhalten bzw. die Konfiguration von JSF-Anwendungen kann mit Konfigurations-

Konfiguration

dateien festgelegt werden. 

»Konfigurationsdateien«, S. 179

Web-Ressourcen sind alle Artefakte, die eine Anwendung benötigt, um das Rendern einer Seite erfolgreich durchführen zu können. 

Ressourcen

»Verwaltung von Ressourcen«, S. 183

Mit AJAX ( Asynchronous JavaScript and XML ) müssen Seiten bei einer Benutzerinter-

AJAX

aktion nicht vollständig neu geladen werden. 

»Asynchronous JavaScript and XML«, S. 189

HTML5 ( Hypertext Markup Language Version 5 ) bietet eine Reihe von Tags und Attributen, die in JSF nicht verfügbar sind. JSF bietet jedoch eine Möglichkeit, auf diese neuen Elemente zuzugreifen. 

»HTML5 Friendly Markup«, S. 197

HTML5

4

1 Aufbau und Gliederung *

WebSocket

JSF unterstützt das WebSocket -Protokoll. 

JSON

»WebSocket«, S. 201

JSON ( JavaScript Object Notation) ist ein leichtgewichtiges Datenaustauschformat. In JSF-Anwendungen kann die Java-API ( Application Programming Interface) für die Verarbeitung von JSON eingesetzt werden. 

Wiederverwendung

»JSON Verarbeitung«, S. 209

JSF bietet eine ganze Reihe von Komponenten an, mit denen die meisten Anwendungsfälle realisierbar sind. In einigen Fällen stoßen die vorhandenen Komponenten jedoch an ihre Grenzen. JSF als Komponentenframework ist jedoch äußerst einfach erweiterbar. 

Faces Flows

»Wiederverwendung«, S. 215

Mit Faces Flows wird die Erstellung von mehreren zusammengehörigen Seiten innerhalb eines Gültigkeitsbereichs ermöglicht. 

Erweiterung

Nahezu jede Aufgabe des Frameworks wird durch ein Modul erledigt, das sich problemlos austauschen lässt. 

Ausblick

»Faces Flows«, S. 237

»JSF-Kern erweitern«, S. 249

Abschließend werden ein Überblick über die aktuelle JSF-Version und ein Ausblick über weitere interessante Themen gegeben. 

»Ausblick«, S. 255

2

Der Schnelleinstieg *

Eine minimale JSF-Seite besteht aus einer XHTML- und zwei Konfigurationsdateien (faces-config.xml und web.xml). Die XHTML-Elemente werden als HTML5 gerendert. Der Schnelleinstieg ist für Leser gedacht, die noch keine Vorkenntnisse in JSF bzw. XHTML ( Extensible HyperText Markup Language) besitzen. Es mag an dieser Stelle

Vorkenntnisse

verwundern, dass XHTML-Kenntnisse notwendig sind, um JSF-Anwendungen zu entwickeln. Dies liegt daran, dass Facelets als standardmäßige VDL ( View Declaration Language) in JSF-Anwendungen eingesetzt wird. Eine detailliertere Beschreibung zu Facelets wird im Laufe des Buchs gegeben. Bei Verwendung von Facelets wird üblicherweise XHTML verwendet. In diesem Buch wird JSF in der Version 2.3 verwendet. Eine vollständige Auflistung aller verfügbaren Klassen, von denen einige in diesem Buch verwendet werden, wird in [Orac18] gegeben. Um einen ersten Einblick in die Syntax von JSF-Anwendungen zu erhalten, wird an dieser Stelle ein »Hallo Welt«-Beispiel gezeigt. Es werden insgesamt zwei Konfigurationsdateien faces-config.xml und web.xml benötigt. Ferner werden eine Java-Klasse

JSF-Version

Motivierendes Beispiel HalloWelt

JSFActivationBean und eine JSF-Seitendeklaration index.xhtml erstellt. Die Dateien wer-

den im Folgenden beschrieben.



faces-config.xml

Zur »Aktivierung« von JSF ist die Erstellung einer Konfigurationsdatei faces-config.xml im Verzeichnis WEB-INF notwendig (alternativ: Erstellung einer speziell annotierten Klasse). Die Angabe bezeichnet die XML-Deklaration ( Extensible Markup Language). Es handelt sich dabei um eine Auszeichnung, mit der jede XML-Datei beginnen soll. Sie beschreibt u. a. die verwendete XML-Version und den verwendeten Zeichensatz. Durch Angabe von version="2.3" wird die JSF-Version festgelegt. Die weiteren Parameter geben den Namensraum an, in dem die verfügbaren Elemente einer JSF-Seitendeklaration definiert sind. @FacesConfig(version = FacesConfig.Version.JSF_2_3) @ApplicationScoped public class JSFActivationBean { }

Zur »Aktivierung« von JSF ist die Erstellung einer speziell annotierten Klasse notwendig (alternativ: Erstellung einer Konfigurationsdatei faces-config.xml). Initial wird JSF 2.3 in einem »JSF 2.2-Kompatibilitätsmodus« verwendet. Dies betrifft beispielsweise die Verwaltung von Objekten im Anwendungsserver mittels CDI ( Contexts

and Dependency Injection). Für die Verwendung von JSF 2.3 ist eine beliebige Klasse mit der Annotation @FacesConfig mit dem Versionsparameter FacesConfig.Version.JSF_2_3 im Gültigkeitsbereich ApplicationScoped zu erstellen. Der Name dieser Klasse kann beliebig gewählt werden.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_2

JSF Activation Bean

6

2 Der Schnelleinstieg *

Hinweis

Im Verlauf des Buchs werden sowohl Annotationen als auch Konfigurationen in der Datei faces-config.xml in geeigneter Weise eingesetzt.

web.xml



javax.faces.PROJECT_STAGE Development

index.xhtml

Die Datei web.xml wird im Verzeichnis WEB-INF gespeichert. Durch den Kontextparameter javax.faces.PROJECT_STAGE mit dem Wert Development werden automatische Meldungen mittels der JSF-Komponente h:messages in der Ansicht ausgegeben. Die Startseite soll index.xhtml sein. Dies wird im Bereich ... festgelegt.

Hinweis

Die Klasse FacesServlet kann automatisch als der Bearbeitungsklasse für die Anfragen gesetzt werden. Voraussetzung hierfür ist das Vorhandensein einer Konfigurationsdatei faces-config.xml oder mindestens einer Klasse mit einer JSF-Annotation (z. B. @FacesConfig). Hierdurch kann der Bereich ... entfallen. Ebenfalls optional ist die Ausprägung des Mappings. Sofern der Bereich ... nicht gesetzt ist, werden automatisch alle *.xhtml-Dateien an das FacesServlet gesendet.

index.xhtml



Hallo Welt



Im DOCTYPE-Bereich ist kein Verweis zu einer HTML-DTD ( Document Type Definition) gegeben, daher handelt es sich um eine HTML5-Seite. HTML5 basiert nicht auf SGML ( Standard Generalized Markup Language), daher ist die Angabe einer Dokumententypdefinition nicht notwendig. Der Ausgabetext Hallo Welt wird durch die JSF-Komponente h:outputText festgelegt.

7

2 Der Schnelleinstieg *

Durch die Verwendung von Facelets als VDL ist die DOCTYPE-Angabe nicht aus-

Hinweis

schlaggebend: Es wird immer HTML5-Quellcode gerendert. Es wäre ebenso möglich, die folgende Dokumententyp-Definition aus dem XHTML-Standard zu verwenden:

Hierbei wird im DOCTYPE-Bereich festgelegt, dass auf die öffentlich verfügbare HTML-DTD ( Document Type Definition) Bezug genommen wird. Die DTD definiert die Dokumentenstruktur. Weiter wird festgelegt, dass XHTML in der Version 1.0 und in der Variante Transitional genutzt wird und dass die verwendeten Elementund Attributnamen in der Sprache EN definiert wurden. Im html-Bereich wird die Sprache der XHTML-Seite festgelegt und die verwendeten Tag -Bibliotheken werden angegeben. Der Aufruf der Seite wird in Abb. 2.0-1 dargestellt.

Ergebnis

Abb. 2.0-1: Beispiel Hallo Welt.

Der vorhandene Quelltext des Aufrufs sieht folgendermaßen aus:

Hallo Welt

Hallo Welt

An dem kleinen Beispiel ist erkennbar, dass zwar die JSF-Seitendeklaration mit XHTML- und JSF-Tags definiert wird, das erzeugte Ergebnis jedoch als HTML5 dargestellt wird. In dem Beispiel wird die Datei index.xhtml als »JSF-Seitendeklaration« bezeichnet. Es werden XHTML- und JSF-Tags verwendet. Im weiteren Verlauf des Buchs wird vereinfacht der Begriff »JSF-Seite« verwendet.

Hinweis

3

Grundlagen der Webentwicklung *

Die Grundlagen der Webentwicklung mit Java werden in verschiedenen JSRs ( Java Spe-

cification Request ) definiert. Dazu gehören beispielsweise »Java 8 SE« (JSR 337), »Expression Language 3.0« (JSR 341) und »JavaServer Faces 2.3« (JSR 372). Ein JSR durchläuft verschiedene Status und kann einen bestimmten Stand erreicht haben. Webanwendungen können verschiedene Architekturen besitzen. Die Inhalte werden in den folgenden Kapiteln beschrieben: 

»Java Specification Request«, S. 9



»Architektur von Webanwendungen«, S. 10

3.1

Java Specification Request **

Ein JSR ( Java Specification Request ) durchläuft verschiedene Status und kann einen bestimmten Stand erreicht haben. JSF ist zunächst eine Spezifikation. Neue Anforderungen an bestehende Spezifikationen werden in einem JSR beschrieben. Für die Aufnahme neuer Sprachelemente oder der API-Erweiterung ( Application Programming Interface) wird ein neuer JSR erstellt. Im Rahmen des Spezifikationsprozesses wird ein JSR von Experten verschiedener Bereiche geleitet. Die aktuelle Spezifikation von JSF 2.3 wird im JSR 372 beschrieben [JCP17]. Die Spezifikationen können sich in unterschiedlichen Status befinden (vgl. Tab. 3.1-1). Status

Beschreibung

Active

Eine Spezifikation, bei der innerhalb der letzten 12 Monate ein Meilenstein veröffentlicht wurde.

Final

Eine Spezifikation, bei der innerhalb der letzten 12 Monate ein Final Release veröffentlicht wurde.

Maintenance

Eine Spezifikation, bei der zuletzt ein Maintenance Review- oder ein Maintenance Release-Meilenstein vor mehr als einem Jahr erstellt wurde.

Inactive

Eine Spezifikation, bei der kein Final Release oder Maintenance Release und innerhalb des letzten Jahres kein Meilenstein erstellt wurde.

Withdrawn

Eine Spezifikation, die vor der Veröffentlichung eines Final Release zurückgezogen wurde.

Rejected

Eine Spezifikation, die in einer der Abstimmungsebenen nicht akzeptiert wurde.

Dormant

Eine Spezifikation, die als »inaktiv« abgestimmt wurde oder die ihre natürliche Lebensdauer erreicht hat.

Tab. 3.1-1: JSR Status.

Dadurch wird der allgemeine Status eines JSR beschrieben. Zusätzlich kann auch der aktuelle Stand im Spezifikationsprozess definiert werden (vgl. Tab. 3.1-2).

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_3

10

3 Grundlagen der Webentwicklung *

Stand

Beschreibung

JSR Review

Zwei bis vier Wochen lang kann sich jeder mit einer Internetverbindung die Spezifikation anschauen und kommentieren.

Early Draft Review

Ein bis drei Monate kann die Öffentlichkeit einen frühzeitigen Spezifikationsentwurf begutachten und kommentieren.

Public Review

Ein bis drei Monate kann die Öffentlichkeit den Spezifikationsentwurf begutachten und kommentieren.

Proposed Final Draft

Der Spezifikationsentwurf wird genutzt, um eine Referenzimplementierung zu erstellen.

Final Release

Die Spezifikation ist in ihrer endgültigen Version verabschiedet und kann nun implementiert werden.

Maintenance Review

Mindestens einen Monat vor der Fertigstellung einer Minor Revision (geringfügige Änderungen an der Spezifikation), bei der Mitglieder und die Öffentlichkeit die vorgeschlagenen Änderungseinträge begutachten und kommentieren können.

Inactive

Inaktive Spezifikationen haben den Final Release nicht erreicht oder es wurden innerhalb der letzten 12 Monate keine Meilensteine veröffentlicht.

Dormant

Diese Kennzeichnung verdeutlicht, dass keine weitere Arbeit in die Spezifikation investiert wird, beispielsweise weil sie durch Abstimmung von der Liste der aktiven Spezifikationen entfernt wurde.

Withdrawn

Dies kennzeichnet Spezifikationen, die zunächst begonnen wurden, aber im weiteren Verlauf von der Liste der aktiven Projekte entfernt wurden, da kein Mitglied sich bereiterklärt hat, die federführende Rolle zu übernehmen.

Rejected

Spezifikationen können durch den ausführenden Ausschuss abgelehnt werden. Tab. 3.1-2: JSR Stand.

Implementierungen

Nachdem die Spezifikation fertiggestellt wurde und den Final Release -Status erreicht hat, kann diese von den jeweiligen Anbietern implementiert werden. Im Fall von JSF sind dies beispielsweise Mojarra oder Apache MyFaces. Bei Mojarra handelt es sich um eine Referenzimplementierung, also um eine Implementierung, die als Referenz für alle anderen Implementierungen angesehen wird. Mojarra wird im Rahmen des Payara-Anwendungsservers eingesetzt. Apache MyFaces ist eine Implementierung der Apache Software Foundation.

3.2 Architektur von Webanwendungen * Webanwendungen können verschiedenartig strukturiert und aufgeteilt werden. Die zugrundeliegende Architektur legt fest, wie die einzelnen Komponenten einer Webanwendung zusammenspielen. Genauso wie übliche Java-Anwendungen gibt es auch für Webanwendungen verschiedene Architekturmuster. An dieser Stelle werden die Architekturmuster Model 1 und

Model 2 behandelt. Bei Model 1 -Webanwendungen greift der Client direkt auf die entsprechenden Views zu. Bei Model 2 -Webanwendungen erfolgt der Zugriff der Clients nicht direkt auf die Views, sondern immer auf den Controller der Webanwendung. Model 2 -Muster werden auch MVC-Muster ( Model-View-Controller-Muster) genannt.

3.2 Architektur von Webanwendungen *

11

Model 1 Die Ablaufsteuerung der Webanwendung von Seite zu Seite wird bei Model 1 -Anwendungen über direkte Verweise realisiert. Das heißt, dass in einer View ein Verweis vorhanden ist, der auf eine andere View verweist. Dieser Ablauf wird in der Abb. 3.2-1 illustriert.

Abb. 3.2-1: Ablauf Model 1 (allgemein).

Der Client ruft jede View direkt auf. Dies kann über Verweise oder direkte Eingabe in der Adresszeile geschehen. In der Abb. 3.2-2 wird der Ablauf im Kontext von JSPAnwendungen erläutert. Der Client ruft die Seite 1.jsp auf (»Anfrage 1«). Diese Seite nutzt ein Objekt der Klasse Benutzer, um dort Daten zu hinterlegen. Um die Klasse Benutzer zu nutzen, erzeugt 1.jsp ein Objekt der Klasse und speichert dort Daten ab, die aus dem Request ermittelt

wurden (»befüllt«). Anschließend generiert die JSP-Seite ein entsprechendes HTML-Dokument als Antwort und liefert dieses an den Client zurück. Dieses generierte HTMLDokument enthält einen Verweis auf 2.jsp. Der Benutzer klickt diesen Verweis an (»Anfrage 2«) und ruft 2.jsp auf. Diese Seite nutzt ebenfalls das von 1.jsp erzeugte Objekt der Klasse Benutzer und generiert wiederum ein HTML-Dokument. Dieses HTML-Dokument wird anschließend an den Client zurückgeliefert. Üblicherweise besteht eine Model 1-Webanwendung (aus der Client-Sicht gesehen) ausschließlich aus JSP-Seiten bzw. Servlets . Das hat zur Folge, dass die JSP-Seiten und

Servlets nicht nur als View , sondern auch als Controller genutzt werden. JSP-Seiten erfüllen hier also nicht nur die Aufgabe, HTML zu generieren und zurückzugeben (als reine View ), sondern rufen auch die Geschäftslogik auf, erzeugen die auf der Seite benötigten JavaBeans und bestimmen, welche JSP-Seite als Nächste angezeigt wird (Ablaufsteuerung). Bei den vorgenannten Aufgaben handelt es sich um Dinge, die üblicherweise von einem Controller erledigt werden. Die JSP-Seiten erledigen also auch folgende (Controller -)Aspekte:

Technischer Aufbau

12

3 Grundlagen der Webentwicklung *

Abb. 3.2-2: Ablauf Model 1 im Kontext von JSP-Anwendungen.

Einschränkungen

 

Anwendungs-/Geschäftslogik Ablaufsteuerung



Erzeugen/Befüllen der benutzten JavaBeans

Zwangsläufig entstehen dadurch bei zunehmender Größe der Anwendung immer komplexere JSP-Seiten und Servlets, da keine strikte Trennung zwischen den einzelnen Aufgabengebieten besteht. Es besteht die Gefahr, den Überblick zu verlieren und eine schwer wartbare Anwendung zu erschaffen.

Fazit

Das bedeutet nicht, dass der Model 1 -Ansatz grundsätzlich abzulehnen ist. Bei kleinen Anwendungen oder Prototypen, die nach der Präsentation verworfen werden, kann

Model 1 ein effizientes Vorgehen ermöglichen. Bei größeren Anwendungen, die über einen längeren Zeitraum gewartet werden sollen, ist Model 1 eher unzureichend. Hier muss eine stärkere Trennung der einzelnen Aufgabengebiete erfolgen. Diese Problematik adressiert Model 2.

Model 2 Bei Model 1 -Webanwendungen greift der Client direkt auf eine View zu. Bei Model 2 Anwendungen gibt es hingegen eine zentrale Stelle, die stattdessen aufgerufen wird und den Aufruf weiterleitet. Dieser Ablauf wird in der Abb. 3.2-3 illustriert. Der Benutzer kommuniziert mit dem Controller, er schickt also seine Requests direkt dorthin. Der Controller erzeugt und befüllt das Model. Anschließend delegiert er die Ausgabeerzeugung an die View, die Zugriff auf die von dem Controller erzeugten

Model -Objekte hat. Im Kontext von JSP-Anwendungen ist die zentrale Stelle, die der Client aufruft, meist ein Servlet (Java-Klasse, die die Schnittstelle javax.servlet.Servlet implementiert). Wegen dieser Aufgabe innerhalb der Webanwendung wird es Front-Controller-Servlet ge-

13

3.2 Architektur von Webanwendungen *

Abb. 3.2-3: Ablauf Model 2 (allgemein).

nannt. Dieses Servlet generiert in der Regel selbst kein HTML, sondern nutzt dazu beispielsweise Facelets, JSP-Seiten oder Servlets. Der Client merkt prinzipiell nichts von diesem Vorgehen. Die Abb. 3.2-4 stellt dar, wie dieselbe Anwendung, die bei Model 1 dargestellt wurde, mit einer Model 2 -Architektur funktioniert. Im direkten Vergleich der Grafik mit der Abb. 3.2-2 ist klar zu erkennen, dass Aufgaben, die vorher von jeder JSP-Seite einzeln erledigt wurden, jetzt durch das Front-

Vergleich mit Model 1

Controller-Servlet durchgeführt werden. In der Abb. 3.2-4 sieht man, dass der Benutzer nicht mehr direkt auf die JSP-Seiten zugreift. Stattdessen ruft er immer das Front-Controller-Servlet auf, das wiederum die

Kein direkter Zugriff auf JSP-Seiten

JSP-Seiten nutzt. Beim ersten Aufruf nutzt das Front-Controller-Servlet in diesem Fall die Seite 1.jsp. Bei dem zweiten Aufruf wird (durch Klick auf einen entsprechenden Verweis) wieder das Front-Controller-Servlet aufgerufen, dieses Mal nutzt das Servlet jedoch 2.jsp. Bei der Model 2 -Webanwendung wird außerdem das Erzeugen und Befüllen der JavaBean -Objekte vom Front-Controller-Servlet übernommen, sodass dies nicht mehr in den Views implementiert werden muss. Durch die zentrale Rolle des Front-Controller-Servlets ist es einfach festzulegen, welche

Erzeugen und Befüllen durch Front-Controller

Ablaufsteuerung

JSP-Seite wann aufgerufen werden soll (Ablaufsteuerung). Model 2 ist übrigens nicht darauf festgelegt, JSP-Seiten zu verwenden, um das HTML zu generieren. Genauso gut wäre es möglich, eine andere Art von Dateien zu verwenden, die die View beschreiben (beispielsweise XML-Dateien wie bei Facelets ). Der wichtigste Unterschied zur Model 1 -Architektur ist die klare Abgrenzung der einzelnen Aufgabenbereiche. Dadurch lassen sich entsprechende Code-Teile leichter lokalisieren und es sinkt das Risiko, dass ein unwartbarer »Monolith« entwickelt wird.

Fazit

14

3 Grundlagen der Webentwicklung *

Abb. 3.2-4: Ablauf Model 2 im Kontext von JSP-Anwendungen.

Wenn man sich die letzten beiden Abbildungen ansieht, stellt man fest, dass viele Funktionen des Front-Controllers eher allgemeiner Natur sind und nur lose mit der Geschäftslogik zusammenhängen. Deshalb ist es durchaus denkbar, einen universellen

Front-Controller zu verwenden, der mittels einer Konfigurationsdatei an die speziellen Anwendungsbedürfnisse angepasst werden kann. Diese Konfigurationsdatei enthält dann Informationen wie Navigationsabläufe (von welcher Seite wird auf welche andere verwiesen) oder Informationen über das zu erzeugende Model. Ein allgemeingültiger Front-Controller muss nicht eigenständig entwickelt werden. Hier setzen Java-Webframeworks wie JSF an und liefern einen allgemeinen Front-Controller (neben vielen weiteren Annehmlichkeiten).

4

Java-Webentwicklung mit Servlets und JSPSeiten *

Die Entwicklung von JSF-Anwendungen unterscheidet sich von der Entwicklung gewöhnlicher Java-Webanwendungen mit Servlets und JSP-Seiten. Der grobe Aufbau einer Java-Webanwendung mit Model 2 -Architektur wird in der

Einführung

Abb. 4.0-1 kurz erläutert.

Abb. 4.0-1: Aufbau einer Webanwendung.

Bei Model 1 -Architekturen wäre auch ein direkter Zugriff auf die JSP-Seiten möglich. Grundsätzlich wird zunächst ein Web Container benötigt (z. B. Tomcat oder Payara), in dem die Webanwendung publiziert werden kann. Die Webanwendung wiederum besteht aus verschiedenen Elementen. In der Abb. 4.0-1 werden die wichtigsten aufgeführt: Eine Konfigurationsdatei (web.xml), ein oder mehrere Servlets und eine oder mehrere JSP-Seiten. Mit JSP-Seiten und Servlets lässt sich problemlos eine Webanwendung entwickeln. Der Client (also der Browser) greift auf das Servlet zu und das Servlet wiederum greift auf die JSP-Seiten zu. Das heißt natürlich, dass hier auch sämtliche technischen Aspekte der Webanwendung implementiert werden. Damit ist gemeint, dass zunächst die Benutzeranforderung (den

Request ) ausgewertet werden muss, um eventuelle Übergabeparameter zu extrahieren, wenn ein Servlet programmiert wird. Dann kann die eigentliche Geschäftslogik ausgeführt und anschließend das HTML-Dokument generiert und an den Client zu-

Ablauf einer Benutzeranforderung ( Request)

rückgegeben werden. Der Vorgang ist in der Abb. 4.0-2 illustriert. Um das Ganze etwas plastischer zu gestalten, wird ein einfaches Beispiel mittels

Beispiel

Servlets, JSP-Seiten und JSF-Seiten realisiert. Die Abb. 4.0-3 zeigt einen exemplarischen Aufruf des Beispiels. Nach der Eingabe eines beliebigen Textes, z. B. »Benutzer«, und Absenden des Formulars, wird der Text »Hallo, Benutzer« angezeigt. Die Werte werden in einer Java-Klasse Benutzer.java gespeichert, die lediglich ein Property name besitzt. public class Benutzer { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_4

Benutzer.java

16

4 Java-Webentwicklung mit Servlets und JSP-Seiten *

Abb. 4.0-2: Ablauf eines Request-/Response-Zyklus.

Abb. 4.0-3: Beispiel zum Vergleich.

In den folgenden Kapiteln wird das Beispiel für die jeweiligen Bereiche realisiert. Servlets

Die Implementierung des Beispiels mit Servlets wird in diesem Kapitel beschrieben. 

JSP-Seiten

Bei der Realisierung des Beispiels mit JSP-Seiten werden unterschiedliche Möglichkeiten gezeigt: JSP-Skripting, JSTL ( JSP Standard Tag Library) und EL ( Expression Language). 

JSF-Seiten

»JSP-Seiten«, S. 18

Im direkten Vergleich wird das Beispiel mit JSF realisiert. 

Fazit

»Servlets«, S. 16

»JSF-Seiten«, S. 23

Die unterschiedlichen Bereiche werden in einem Fazit verglichen. 

»Fazit zu Servlets und JSP-Seiten«, S. 25

4.1 Servlets * Ein Servlet (genauer HTTP-Servlet ) ist eine Java-Klasse, die die Schnittstelle javax.servlet.http.HttpServlet implementiert. Jedes HTML-Element muss mit den Methoden print() bzw. println() erstellt werden. Eine Klasse, die die Schnittstelle javax.servlet.http.HttpServlet implementiert und damit als Servlet fungieren soll, muss in der Konfigurationsdatei web.xml als solches festgelegt werden. Die Schnittstelle HttpServlet erbt von javax.servlet.Servlet. Beispiel

Die Realisierung des vorgestellten Beispiels (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15) mit Servlets wird nun erläutert (Programm WebentwicklungServlets).

17

4.1 Servlets *

public class BeispielServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println(""); out.println(""); out.println("BeispielServlet") out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if (request.getParameter("benutzername")!=null) { out.println("

Hallo, " + request.getParameter("benutzername")+"

"); } out.println(""); } finally { out.close(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } }

Beispiel

BeispielServlet jsf.beans.BeispielServlet 1

BeispielServlet /BeispielServlet

web.xml

In diesem Beispiel sind die einzelnen Schritte ersichtlich, die durchgeführt werden müssen, um den Zyklus von Request und Response zu durchlaufen:

Erklärung

e Zunächst wird das Eingabeformular erzeugt. e Dann wird der Request ausgewertet und entschieden, ob eine Mitteilung ausgegeben wird (ist ein Parameter benutzername gesetzt?).

e Schließlich wird das HTML gerendert.

Servlet .java

18

4 Java-Webentwicklung mit Servlets und JSP-Seiten *

4.2 JSP-Seiten * JSP-Seiten sind eine weitere Möglichkeit der Webentwicklung. Es müssen hiermit keine ausschließlichen Java-Klassen wie bei Servlets verwendet werden, sondern die Programmteile werden innerhalb der HTML-Elemente positioniert und zur Laufzeit ausgewertet. Im Hintergrund werden JSP-Seiten zu Java-Klassen, auch JSP-Klassen genannt, transformiert. Eine JSP-Klasse ist eine Servlet -Klasse, daher kann diese auch einfach als Servlet bezeichnet werden. Dies läuft für den Benutzer jedoch transparent ab. In den folgenden Kapiteln werden unterschiedliche Implementierungen von JSP-Seiten gezeigt: JSP-Skripting, JSTL ( JSP Standard Tag Library) und EL ( Expression Language).  

»JSP-Skripting«, S. 18 »JSP Standard Tag Library«, S. 19



»Expression Language«, S. 21

4.2.1 JSP-Skripting * Beim JSP-Skripting wird der Java-Code innerhalb einer JSP-Seite eingebunden. Es entsteht eine Mischung von HTML-Elementen und Java-Code. JSP-Skripting ist dahingehend eine Verbesserung, da die vollständige Entwicklung einer Java-Klasse, die die Schnittstelle javax.servlet.http.HttpServlet implementiert, nicht mehr notwendig ist. Die benötigten Java-Codezeilen werden direkt an der Stelle des HTMLDokuments eingebunden, an der sie benötigt werden. Es werden verschiedene Arten von JSP-Skriptelementen unterschieden:

Beispiel 1a



Deklarationen



Werden eingesetzt, um Konstanten, Variablen und Methoden zu definieren, die



in einer JSP-Seite verwendet werden sollen. Skriptlets



In diesen Bereichen wird der Java-Code an den Stellen des HTML-Dokuments eingebunden, an denen er benötigt wird.



Ausdrücke



Dient als einfache Ausgabe. Alternativ könnte dies in Skriptlets mittels out.println(...); realisiert werden.



Kommentare



Damit werden JSP-Kommentare definiert, die zwar in den JSP-Seiten sichtbar sind, jedoch nicht in den generierten HTML-Seiten.

Die Realisierung des vorgestellten Beispiels (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15) mit JSP-Skripting wird nun erläutert (Programm WebentwicklungJSP).

index_ jspskripting.jsp



JSP-Skripting



19

4.2 JSP-Seiten *





Hallo,





An dem kleinen Beispiel ist bereits erkennbar, dass JSP-Skripting die Komplexität des Quelltextes erhöht. Für den Bereich der schließenden Klammer »}« muss erneut

Erklärung

ein JSP-Skriptlet erzeugt werden. Alternativ kann auch der gesamte Absatz mittels Java-Code ausgegeben werden:

Beispiel 1b

In diesem Fall wird zwar die erneute Öffnung eines neuen JSP-Skriptlets, das aus-

Erklärung

schließlich eine schließende geschweifte Klammer einfügt, verhindert, jedoch muss explizit auf das Objekt out zugegriffen werden.

4.2.2

JSP Standard Tag Library *

JSTL ist die Abkürzung für JSP Standard Tag Library. Sinn und Zweck dieser Technik ist es, möglichst auf Java-Code innerhalb von JSP-Seiten zu verzichten. Die JSP-Tags der JSTL erfüllen bestimmte Aufgaben, welche normalerweise mithilfe von Java-Code-Blöcken realisiert werden. Zu den Aufgabengebieten zählen unter anderem: 

Ablaufsteuerung (when, otherwise etc.)

 

Schleifen XML-Verarbeitung



Internationalisierung



Benutzung von SQL ( Structured Query Language)

Was können JSPTags ?

Für die meisten Anwendungsgebiete existieren also bereits Tags . Der Einsatz der Tags erleichtert das Entwickeln, Warten und Nachvollziehen von JSP-Seiten. Sollte es für eine bestimmte Aufgabe noch kein Tag geben, ist es auch möglich, eigene

Tags zu entwickeln. Zur Implementierung eigener Tags sind drei Ansätze vorhanden:

Eigene JSP-Tags

20

4 Java-Webentwicklung mit Servlets und JSP-Seiten *

  

Simple Tags Classic Tags Tag Files (ab JSP 2.0)

Für die Ansätze Simple Tags und Classic Tags muss eine eigene TLD-Datei (Tag Libra-

ry Descriptor ) geschrieben werden, die das entsprechende Tag definiert. Innerhalb der TLD-Datei wird dann eine ebenfalls selbst zu entwickelnde Java-Klasse mit dem Tag verknüpft. Diese Java-Klasse muss den Typ javax.servlet.jsp.tagext.SimpleTagSupport bzw. javax.servlet.jsp.tagext.TagSupport erweitern. Sobald diese TLD in eine eigene JSPSeite eingebunden wird, kann das selbst definierte Tag verwendet werden. Für weitere Informationen zu eigenen JSP-Tags wird auf [Wißm09] verwiesen. Die Abb. 4.2-1 illustriert den Zusammenhang zwischen JSP-Seite, TLD und Java-Klasse. JSP - nutzt die TLD

nutzt

TLD - definiert ein Tag

Java-Klasse verweist auf

- nutzt das in der TLD definierte Tag

- implementiert z.B. die Methode doTag() im Falle eines Simple Tags

Abb. 4.2-1: TLD-Konzept vereinfacht dargestellt. Funktionsweise von JSP-Tags

In der JSP-Seite selbst wird kein Java-Code verwendet, sondern nur ein Tag und ein Verweis auf den Tag Library Descriptor. Der Tag Library Descriptor verknüpft das Tag mit einer Java-Klasse. Sobald dieses Tag während des Render -Vorgangs (also der Erstellung des HTML-Dokuments) der JSP-Seite angetroffen wird, wird die entsprechende doTag()-Methode der Java-Klasse aufgerufen, die dann das Rendern des jeweiligen HTML-Codes übernimmt. Diese JSP-Tags lassen sich dann als JAR-Datei ( Java Archive) packen und auch problemlos in anderen Webprojekten einsetzen.

Beispiel

index_ jstl.jsp

Die Realisierung des vorherigen Beispiels (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15) mit JSTL wird nun erläutert (Programm WebentwicklungJSP).



JSTL







21

4.2 JSP-Seiten *





Im Gegensatz zum JSP-Skripting wird kein Java-Code, sondern ausschließlich Tags

Erklärung

verwendet. Ersichtlich sind nicht nur die gewöhnlichen HTML-Tags, sondern auch spezielle JSP-Tags (jsp:useBean ...) aus den entsprechenden Tag -Bibliotheken ( Tag

Library). Die JSP-Lösung ist längst nicht mehr so »technisch« wie die vorhergehende Lösung. Es ist zwar noch nötig, den Parameter aus dem Request zu lesen, also den Request auszuwerten, allerdings wird vom Request abstrahiert: Das Wort Request selbst taucht nicht mehr auf. Im Folgenden wird eine kurze Liste mit den wichtigen Merkmalen dieser JSP-Lösung gezeigt:

e Zum Generieren des HTML-Codes werden JSP-Tags statt Java-Code verwen-

e e e e

det. Der Parameter benutzername wird einfach mit dem Tag jsp:setProperty in das Property name des Objektes benutzer gespeichert, das wiederum mit dem Tag jsp:useBean erzeugt wurde. Es werden Gültigkeitsbereiche benutzt, um Objekte zu speichern. Natürlich reicht es nicht, einfach nur ein Objekt benutzer vom Typ jsf.beans.Benutzer zu erzeugen. Es ist nötig zu sagen, in welchem Gültigkeitsbereich es liegt, also wo es gespeichert werden soll. Es wird die sogenannte EL (Expression Language, siehe »Expression Language«, S. 21) verwendet, um Java-Code auch bei einfachen Auswertungen zu vermeiden. In dieser Lösung ist keine Zeile Java-Code, sondern lediglich die EL-Ausdrücke ${benutzer.name!=null} und ${benutzer.name}.

Insgesamt ist die Lösung also etwas stärker von der darunterliegenden Technik abstrahiert. Für weitergehende Informationen zu dem Thema JSP-Tags wird auf die Quellen [Babl03] und [Horn09] verwiesen.

4.2.3

Expression Language *

Mit der EL (Expression Language ) werden Ausdrücke definiert, die in JSP-Seiten verwendet werden können. Die EL ist eine Skriptsprache, die den einfachen Zugriff auf Objekte erlaubt, die sich in irgendeinem Gültigkeitsbereich befinden. Historisch gesehen wurde EL zunächst in JSTL eingeführt und seit JSP 2.0 als eigenständige Technik übernommen. Die Realisierung des vorherigen Beispiels (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15) mit EL und JSTL wird nun erläutert (Programm WebentwicklungJSP).

Beispiel 1a

22

4 Java-Webentwicklung mit Servlets und JSP-Seiten *

index_el_ mit_jstl.jsp

Erklärung



EL mit JSTL







Hallo, ${benutzer.name}





Da EL in JSP 2.0 übernommen wurde, können die EL-Ausdrücke auch außerhalb von JSTL-Ausdrücken verwendet werden. In diesem Fall betrifft es die Zeile

Hallo, ${benutzer.name}

. Im Vergleich dazu wurde im JSTL-Beispiel die Zeile

verwendet.

Bei dem Ausdruck ${benutzer.name!=null} handelt es sich um einen Ausdruck, der in der EL geschrieben ist. Dieser EL-Ausdruck bezieht sich auf das Objekt benutzer. Dieses könnte im Gültigkeitsbereich page gespeichert und ein Objekt der Klasse jsf.beans.Benutzer sein. Ablauf

Über den Ausdruck ${benutzer.name!=null} wird geprüft, ob die Eigenschaft name des Objekts benutzer ungleich null ist und dann entsprechend der Wert true oder false zurückgegeben. Dazu ist es notwendig, dass die Klasse jsf.beans.Benutzer auch ein Property mit dem Namen name hat und Zugriff auf dieses via Get - (getName()) und

Set -Methoden (setName()) anbietet (JavaBeans -Konvention). Beispiel 1b

Alternativ kann auch vollständig auf JSTL-Ausdrücke verzichtet werden. Die Realisierung des vorherigen Beispiels (siehe »Java-Webentwicklung mit Servlets und JSPSeiten«, S. 15) ohne JSTL-Ausdrücke wird im Folgenden erläutert (Programm WebentwicklungJSP).

index_el_mit_jsp skripting.jsp



EL mit JSP-Skripting





Hallo, ${benutzer.name}





Die JSTL-Ausdrücke choose und when werden durch eine »if-Abfrage« ersetzt. In Kombination mit JSTL ist es mit der EL möglich, innerhalb der JSP-Seite vollständig

Erklärung Vorteile der EL

auf Java-Code zu verzichten. Ohne JSTL können EL-Ausdrücke mit kleinen Teilen Java-Code genutzt werden, um einfache Prüfungen durchzuführen. Dies verbessert die Verständlichkeit der JSP-Seiten. EL-Ausdrücke lassen sich schnell interpretieren. Im Gegensatz zu Java-Code ist sofort offensichtlich, was sie genau abprüfen. Die Prüfung des vorherigen Beispiels, ob ein Wert für name angegeben wurde, sieht

Beispiel 1c

mit Java-Code wie folgt aus: ((Benutzer)pageContext.getAttribute("benutzer")).getName()!=null

Wenn man diesen Code mit dem vorherigen Beispiel vergleicht, wird sofort ersichtlich, dass der EL-Ausdruck im Vergleich zum Java-Code deutlich verständlicher ist.

Erklärung

Die Trennung von Code und Design macht den Code verständlicher und wartbarer. Zudem ist es so auch für Personen mit begrenzten Programmierkenntnissen (z. B. Designer) möglich, JSP-Seiten selbständig zu modifizieren. Auch eine Rollentrennung (Design und Programmierung) wird hierdurch erleichtert. Die EL wird auch bei JSF häufig verwendet. Neben der gezeigten Prüfung auf Ungleichheit (!=) gibt es auch viele andere Operatoren, wie einen »und«- oder einen »oder«-Operator. Bei Zahlen sind auch arithmetische Operationen wie + oder - möglich.

4.3

Weitere Möglichkeiten

JSF-Seiten *

Bei der Entwicklung von JSF-Anwendungen werden Facelets eingesetzt. Zum Vergleich einer JSF-Webanwendung mit einer üblichen Java-Webanwendung wird in der Abb. 4.3-1 der grundsätzliche Aufbau einer JSF-Webanwendung gezeigt. Die Grafik unterscheidet sich etwas von einer üblichen Java-Webanwendung (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15). Da es sich hierbei um eine

Model 2 -Webanwendung handelt, erfolgt der Zugriff ausschließlich auf das Faces Servlet und nicht mehr direkt auf die JSF-Seiten. Beim Faces Servlet handelt es sich um das zuvor erwähnte Front-Controller-Servlet (siehe »Architektur von Webanwendungen«, S. 10). Dieses wird durch das JSF-Framework bereitgestellt.

Grundsätzlicher Aufbau

Vergleich

24

4 Java-Webentwicklung mit Servlets und JSP-Seiten *

Abb. 4.3-1: Aufbau einer JSF Webanwendung.

Neben der web.xml, die von jeder Java-Webanwendung benötigt wird, gibt es eine weitere Konfigurationsdatei namens faces-config.xml. Diese Datei kann zur Konfiguration von JSF-spezifischen Einstellungen verwendet werden. Zwar übernimmt das Faces Servlet nur allgemeine Aufgaben wie das Aufrufen der JSF-Seiten, jedoch ist es natürlich anwendungsabhängig, wann welche JSF-Seite aufgerufen werden soll. Genau derartige Informationen liefert die faces-config.xml-Datei. Beispiel

Damit diese Unterschiede etwas greifbarer werden, wird nachfolgend die Webanwendung, welche bereits in den Ausführungen zu Servlets und JSP-Seiten vorgestellt wurde (siehe »Java-Webentwicklung mit Servlets und JSP-Seiten«, S. 15), nun als JSF-Anwendung umgesetzt (Programm WebentwicklungJSF).

index.xhtml

web.xml

faces-config.xml

Benutzer.java





index.xhtml



@Named("benutzer") @SessionScoped public class Benutzer implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

25

4.4 Fazit zu Servlets und JSP-Seiten *

Der Inhalt der JSF-Seite index.xhtml enthält keinen Java-Quellcode. Die Einbindung

Erklärung

davon ist hier sogar verboten. Es dürfen lediglich JSF-Tags und EL-Ausdrücke verwendet werden. Die hier verwendeten EL-Ausdrücke werden – im Gegensatz zu den EL-Ausdrücken in den JSP-Beispielen – mit einem »#« eingeleitet. Diese werden im weiteren Verlauf des Buches erklärt (siehe »Expression Language«, S. 33). In der Klasse Benutzer.java wird die Annotation @Named("benutzer") verwendet, um ein Objekt dieses Typs innerhalb der JSF-Seite zur Verfügung zu stellen. Die Annotation @SessionScoped legt hierbei fest, dass das Objekt im Kontext einer Sitzung gespeichert wird.

4.4

Fazit zu Servlets und JSP-Seiten *

JSP-Seiten mit EL oder JSTL abstrahieren stärker von der darunterliegenden Technik als Servlets oder JSP-Skripting. Mit JSF wird noch stärker von der Technik abstrahiert. Um die Motivation zu verstehen, die hinter der JSF-Technik steckt, ist es hilfreich, sich

Motivation für JSF

die historische Entwicklung der Vorgängertechniken und ihre Schwächen anzusehen. Generell lässt sich sagen, dass man im Regelfall durch den Einsatz von EL bzw. JSTL bessere Resultate erzielen wird, als mit Servlet -Technik. Dafür gibt es mehrere Gründe:

Vergleich JSP/Servlet

Stärkere Abstrahierung von den zugrundeliegenden technischen Gegebenheiten: Es ist beispielsweise kein expliziter Zugriff auf den Request mehr nötig. Bessere Trennung der Geschäftslogik von der View : Der Java-Code kann relativ einfach in Java-Klassen ausgelagert werden, während die JSP-Seite selbst nur die View darstellt. Bei größeren Projekten kann es in Servlet -Klassen schnell unübersichtlich werden, da hier HTML und Java-Code in einer Klasse gemischt werden. Mit Projekt-Richtlinien

Schwächen des Servlet -Ansatzes

könnte dieses Problem abgemildert werden, jedoch müsste vermutlich, früher oder später, ein kleines Web-Framework entwickelt werden. Aufgrund der Vielzahl bestehender Frameworks ist dies nicht nötig. Es ist sinnvoller, von vornherein ein geeignetes Framework auszuwählen, das technische Aspekte kapselt. Der Einsatz von JSP-Skripting, JSTL oder EL ist hier sicherlich eine deutliche Verbesserung gegenüber der Servlet -Technik. Jedoch gibt es auch hier noch einige Aspekte, um die man sich kümmern muss: Transfer von Daten aus dem Request in Objekte. Erzeugung von Objekten. Generieren von HTML (obwohl dies schon teilweise von der JSTL übernommen wird). Ablaufsteuerung (welche JSP-Seite wird nach welcher Benutzeraktion aufgerufen). JSF abstrahiert noch mehr von der darunterliegenden Technik und bietet Lösungen für die oben genannten Probleme und dies soll letztendlich Motivation genug sein, um sich in den folgenden Kapiteln eingehend mit JSF zu beschäftigen.

Schwächen des JSP/JSTL-Ansatzes

5

JavaServer Faces *

Die Entwicklung mit JSF stellt sich etwas anders dar, als die Entwicklung mit

Servlets oder JSP-Seiten. JSF ist ein Framework basierend auf dem Model 2 Konzept. Die Entwicklung mit JSF ist weiter von der darunterliegenden Webtechnik entfernt, als bei der Java-Webentwicklung mit Servlets und JSP-Seiten. Obwohl die Verwendung von EL bzw. JSTL bereits eine Möglichkeit darstellt, bis zu einem gewissen Grad von

Unterschiede zur Entwicklung ohne JSF

den technischen Details einer Webanwendung zu abstrahieren, versucht JSF hier noch einen Schritt weiter zu gehen. Bei JSTL werden Tags verwendet, die hauptsächlich die Aufgabe haben, bestimmte HTML-Anteile zu generieren. Bei JSF verwendet man Komponenten, die zwar ähnlich einfach wie die Tags zu verwenden sind, jedoch noch mehr Möglichkeiten bieten, als mit einfachen JSTL-Tags. Ein weiterer Punkt ist, dass bei einer einfachen Webarchitektur direkt auf die View (also die Servlets und JSP-Seiten, die das HTML generieren) zugegriffen wird. Man spricht hierbei auch von Model 1 Webanwendungen. Im Gegensatz dazu wird bei JSF nicht direkt auf die View zugegriffen, sondern indirekt über ein sogenanntes Front-Controller-Servlet . Das heißt, der Benutzer ruft nicht direkt eine JSF-Seite auf, sondern zunächst das Front-Controller-Servlet, welches wiederum die JSF-Seite aufruft und das generierte HTML an den Benutzer zurückgibt. Hierbei spricht man von Model 2 -Webanwendungen. Diese Webanwendungen sind an das von der Fatclient -Entwicklung bekannte MVC-Muster ( Model-View-Controller) angelehnt. Da sich dieses Muster jedoch nicht eins zu eins auf Webanwendungen anwenden lässt, spricht man hierbei manchmal auch vom MVC-2-Muster. Die folgenden Kapitel erklären die wesentlichen Ansätze und grundlegenden Techniken im Kontext von JSF: 

»Historie«, S. 27



»Architektur von JSF«, S. 29

 

»View Declaration Language«, S. 30 »Tag-Bibliotheken«, S. 31



»Expression Language«, S. 33

 

»Managed Beans«, S. 35 »Annotationen vs. Konfigurationsdatei«, S. 54



»Bindings«, S. 56

5.1

Historie *

JSF 1.0 wurde 2004 im JSR 127 beschrieben. Im Laufe der Jahre kamen einige Versionen dazu, bis die derzeit aktuelle Version 2.3 veröffentlicht wurde. JSF hat das Ziel, von der darunterliegenden Technik zu abstrahieren und bereits vorhandene Ansätze bei der Java-Webentwicklung zu standardisieren. Die erste JSF-Version 1.0 wurde im März 2004 freigegeben ( Final Release 1 ) und war noch nicht Bestandteil der Java EE-Spezifikation ( Enterprise Edition).

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_5

JSF 1.0

28

5 JavaServer Faces *

JSF 1.1

Diese Version war noch stark fehlerbehaftet und so wurde zwei Monate später die Version 1.1 ( Final Release 2 ) freigegeben. In dieser Version wurde die Spezifikation nicht geändert, sondern es wurden lediglich Fehler beseitigt.

JSF 1.2

JSF 1.2 wurde anschließend im Mai 2006 im JSR 252 freigegeben und auch in die Java EE-Spezifikation 5 aufgenommen. Folgende Funktionalitäten kamen hinzu (Auszug): 

Expression Language : In den vorherigen Versionen ist eine EL-Version vorhanden, die speziell für die Bedürfnisse von JSF erstellt wurde. Diese ist unabhängig von der EL, die bei der JSP-Technologie verwendet wird. Dies liegt darin begründet, dass ältere JSF-Versionen (vor 1.2) auf der JSP-Version 2.0 aufsetzen und die dort vorhandene EL musste weiterhin gültig bleiben. In JSF 1.2 wurden nun diese



beiden EL-Versionen vereinheitlicht (siehe »Expression Language«, S. 33). Nachrichten an Komponenten: Es können nun Nachrichten an bestimmte Komponenten verknüpft werden. Dies ist besonders bei Validierungsfehlern interessant, da nun direkt an der Komponente der Fehler dargestellt werden kann.

JSF 2.0

Durch die Version 1.2 erfuhr JSF eine weite Verbreitung. Die nächste Version 2.0 wurde im Juni 2009 im JSR 314 spezifiziert und in die Java EE-Spezifikation 6 aufgenommen. Folgende Funktionalitäten kamen hinzu (Auszug):  

 

   JSF 2.1

Facelets : JSP wurde als Seitendeklarationssprache durch Facelets ersetzt (siehe »View Declaration Language«, S. 30). AJAX ( Asynchronous JavaScript and XML ) : Dabei handelt es sich um eine asynchrone Datenübertragung zwischen dem Client (Webbrowser) und dem Server (Webserver) (siehe »Asynchronous JavaScript and XML«, S. 189). Komponenten: Eigene Komponenten können ohne Java-Code entwickelt werden (siehe »Wiederverwendung«, S. 215). Validierung: Angaben zu Validierungen können direkt an den Stellen festgelegt werden, an denen sie gebraucht werden, z. B. als Annotationen bei den Attributen (siehe »Validierung«, S. 109). Annotationen: Durch den breiten Einsatz von Annotationen können JSF-Anwendungen sehr einfach konfiguriert werden. Ressourcen: Die Verwaltung von Ressourcen, z. B. CSS ( Cascading Style Sheets), wurde deutlich vereinfacht (siehe »Verwaltung von Ressourcen«, S. 183). Gültigkeitsbereich: Ein neuer Gültigkeitsbereich View wurde hinzugefügt.

Ein Jahr später (Oktober 2010) wurde die Version 2.1 veröffentlicht. In dieser Version wurden hauptsächlich Fehler bereinigt und die Spezifikation wurde an einigen Stellen nachbearbeitet. Folgende Funktionalität kam hinzu (Auszug): 

JSF 2.2

JSP Document Syntax : Dateien, die in der JSP Document Syntax (Dateiendung .jspx) geschrieben wurden, werden wie Facelets behandelt.

Die JSF-Version 2.2 wurde im Mai 2013 im JSR 344 spezifiziert und in die Java EESpezifikation 7 aufgenommen. Folgende Funktionalitäten kamen hinzu (Auszug):  

Faces Flows : Mehrere Webseiten können als Abläufe in Flows zusammengefasst werden (siehe »Faces Flows«, S. 237). HTML5-Unterstützung: HTML5 verfügt über eine Reihe neuer Attribute, die in JSF 2.1 nicht unterstützt und damit nicht dargestellt werden. JSF 2.2 ermöglicht nun die Festlegung von »pass through attributes «, die einfach im generierten Dokument dargestellt werden (siehe »HTML5 Friendly Markup«, S. 197).

29

5.2 Architektur von JSF *



Dateien hochladen: Es ist nun mit JSF möglich, eine Datei mittels der Komponente h:inputFile hochzuladen (siehe »Dateiupload«, S. 143).

Die aktuelle JSF-Version 2.3 wurde im April 2017 im JSR 372 spezifiziert und in die Java

JSF 2.3

EE-Spezifikation 8 aufgenommen. Folgende Funktionalitäten kamen hinzu (Auszug): 

CDI-Managed Beans statt JSF-Managed Beans : Die Verwaltung der Objekte obliegt dem Anwendungsserver und nicht dem JSF-Framework (siehe »Managed Beans«, S. 35).



Java-Unterstützung: Sprachelemente aus der Java-Version 8 können verwendet werden.



WebSocket-Integration: Native WebSocket-Verwendung ist möglich (siehe



»WebSocket«, S. 201). Validierung: Validierungen auf Klassenebene können ausgeführt werden (siehe »Validierung«, S. 109).

5.2

Architektur von JSF *

JSF ist ein Model 2-Framework – dies ist einfach an dem zentralen Front-Controller-Servlet zu erkennen, das bei JSF Faces Servlet heißt. JSF funktioniert wie eine Model 2 -Anwendung (siehe »Architektur von Webanwendungen«, S. 10). Über Faces Servlet erfolgen sämtliche Ein- und Ausgaben. In der Konfigurationsdatei web.xml ist die Festlegung von Faces Servlet als zu nutzendes Servlet nicht zwingend erforderlich. Die Klasse FacesServlet kann automatisch als der Bearbeitungsklasse für die Anfragen gesetzt werden, sofern die Konfigurationsdatei facesconfig.xml oder mindestens einer Klasse mit einer JSF-Annotation (z. B. @FacesConfig) vorhanden ist. Hierdurch kann der Bereich ... entfallen. Ebenfalls optional ist die Ausprägung des Mappings. Sofern der Bereich ... nicht gesetzt ist, werden automatisch alle *.xhtml-Dateien an das FacesServlet gesendet. Optional kann in der Konfigurationsdatei web.xml die Nutzung von Faces Servlet

Konfiguration der web.xml

Beispiel

folgendermaßen festgelegt werden:

Faces Servlet javax.faces.webapp.FacesServlet 1

Faces Servlet *.xhtml

web.xml

Eine JSF-Seite kann in einem Web Container zur Verfügung gestellt werden, auch

Hinweis

wenn das Faces Servlet nicht eingerichtet ist, jedoch wird in diesem Fall dann lediglich der Text einer JSF-Seite angezeigt, ohne dass sie verarbeitet wird. Die Abb. 5.2-1 zeigt eine modifizierte Model 2 -Abbildung, welche den Ablauf einer JSF-Anwendung noch einmal schematisch darstellt.

Vergleich mit Model 2

30

5 JavaServer Faces *

Webserver Facelets HTML 1.xhtml (*)

1.xhtml

delegiert

Anfrage 1 Client

liest

Faces Servlet

Anfrage 2 facesconfig.xml

nutzt

JavaBean

erzeugt befüllt

Benutzer

delegiert Facelets

HTML 2.xhtml

2.xhtml (*)

liest

(*) Mit Navigationsmöglichkeit zu Folgeseiten Abb. 5.2-1: Ablauf Model 2 im Kontext von JSF-Anwendungen.

Bei der Abbildung wird noch einmal deutlich, dass das Faces Servlet im Kontext von JSF das Front-Controller-Servlet darstellt. Streng genommen erzeugt der CDI-Container die Managed Beans und nicht das Faces Servlet. Dieser Sachverhalt wird im weiteren Verlauf näher beschrieben. Die einzelnen JSF-Seiten (1.xhtml und 2.xhtml) werden mittels XHTML beschrieben und nutzen Facelets als Seitendeklarationssprache. Der Client erhält als Antwort HTML-Seiten (genauer HTML5-Seiten).

5.3 View Declaration Language * Als VDL ( View Declaration Language) wird die Seitendeklarationssprache einer bestimmten Technologie bezeichnet. Bei JSF sind dies seit JSF 2.0 die Face-

lets. Facelets vs. JSP

Facelets ersetzt JSP als Seitendeklarationssprache für JSF-Anwendungen. Dies hat vielfältige Gründe. Wie bereits in den vorherigen Kapiteln beschrieben, abstrahiert JSF stärker von der darunterliegenden Technologie als JSP. Seit JSF 2.0 wird Facelets als primäre Seitendeklarationssprache verwendet. Vor JSF 2.0 wurde JSP als primäre Seitendeklarationssprache verwendet. Ziel war es, den Entwicklern den Umstieg von JSP nach JSF so leicht wie möglich zu machen.

Lebenszyklus

Facelets und JSP durchlaufen sehr unterschiedliche Lebenszyklen. Eine JSP-Seite wird bei der ersten Anfrage geladen. Dabei wird die JSP-Seite zu einer JSP-Klasse ( Servlet ) transformiert und bleibt solange verfügbar, wie der Webserver gestartet ist und die JSP-Seite nicht geändert wurde. Der Lebenszyklus von JSF-Seiten ist deutlich komplexer (siehe »Der JSF-Lebenszyklus«, S. 165). Es werden verschiedene Aufgaben (z. B. Rendern, Validieren, Konvertieren) in einer bestimmten Reihenfolge abgearbeitet.

31

5.4 Tag -Bibliotheken *

Die Situation wird besonders problematisch, wenn die beiden Technologien kombiniert werden. Dies ist dann der Fall, wenn JSF-Elemente und HTML-Elemente gleichzeitig verwendet werden. Die HTML-Elemente werden beim Erstellen der View direkt dargestellt, während die JSF-Elemente im Komponentenbaum (siehe »Komponentenbaum«, S. 161) abgebildet und erst am Ende des Lebenszyklus generiert werden. Bei neuen Anwendungen sollte Facelets als Seitendeklarationssprache genutzt werden. Facelets ist eine leichtgewichtige Seitendeklarationssprache, welche mittels XHTML-Elementen die JSF-Seiten und den Komponentenbaum erstellt.

5.4

Tag -Bibliotheken *

Tags sind aus gewöhnlichen XHTML-Seiten bekannt. JSF-Tags sind komplexere Konstrukte, die dazu dienen, eine JSF-Seite aufzubauen. In Tag -Bibliotheken ( Tag Library) werden die Tags definiert. JSF unterstützt verschiedene Tag -Bibliotheken. In Facelets können verschiedene Tag -Bibliotheken verwendet werden. In Tab. 5.4-1 werden die von Facelets unterstützten Tag -Bibliotheken aufgeführt (Auszug). Tag -Bibliothek

URI

Präfix

JavaServer Faces Facelets

http://xmlns.jcp.org/jsf/facelets

ui:

JavaServer Faces HTML

http://xmlns.jcp.org/jsf/html

h:

JavaServer Faces Core

http://xmlns.jcp.org/jsf/core

f:

Pass-through Elements

http://xmlns.jcp.org/jsf

jsf:

Pass-through Attributes

http://xmlns.jcp.org/jsf/passthrough

p:

JSTL Core

http://xmlns.jcp.org/jsp/jstl/core

c:

JSTL Functions

http://xmlns.jcp.org/jsp/jstl/functions

fn:

Tab. 5.4-1: Unterstützte Tag -Bibliotheken.

Je nach benötigter Funktionalität kann eine entsprechende Tag -Bibliothek genutzt werden. Ebenfalls ist eine Kombination der Bibliotheken möglich. Bei dem dargestellten Präfix handelt es sich lediglich um den Standardpräfix. Dieser kann je nach Bedarf auch eine andere Abkürzung erhalten. Für eine Aufstellung aller vorhandenen Tags wird auf [Orac18a] verwiesen. In diesem Buch wird der Standardpräfix des jeweils behandelten Tags immer angegeben. Die JavaServer Faces Facelets Tag Library enthält alle Tags, die für die Nutzung von Templating notwendig sind (siehe »Templating«, S. 81) (Auszug): 

ui:component

 

ui:insert



ui:param

Facelets

ui:repeat

Die JavaServer Faces HTML Tag Library enthält Tags für alle UIComponent-Objekte. Dabei handelt es sich um die Komponenten, die den HTML-Code erzeugen (Auszug):

HTML

32

5 JavaServer Faces *

Core

 

h:head



h:form

 

h:outputText

 

h:message

h:body

h:inputText h:messages

Die JavaServer Faces Core Tag Library enthält Tags für alle Komponenten, die grundlegende Aktionen unabhängig von einem bestimmten Render-Kit ermöglichen (Auszug):

Pass-through Elements

Pass-through Attributes



f:actionListener

 

f:attribute

 

f:loadBundle

JSTL Functions

f:validateLength

Die Pass-through Elements Tag Library enthält Tags für die Unterstützung von HTML5

friendly markup (Auszug): 

jsf:id



jsf:element

Die Pass-through Attributes Tag Library enthält Tags für die Unterstützung von HTML5

friendly markup (Auszug): 

JSTL Core

f:param

p:type

Die JSTL Core Tag Library enthält Tags für oft verwendete Funktionen (Auszug):  

c:catch



c:forEach

 

c:if

c:choose

c:when

Die JSTL Functions Tag Library enthält Tags für Standardfunktionen, bei denen meistens eine Manipulation von Texten durchgeführt wird (Auszug):

Komponente, Renderer, Handler

 

fn:contains



fn:toLowerCase



fn:trim

fn:toUpperCase

Bei einer typischen, reinen JSP-Anwendung, die JSTL verwendet, spricht man ausschließlich von Tags. Bei JSF sind es jedoch nicht nur Tags, die verwendet werden. Das Tag ist vielmehr nur ein Bindeglied zwischen JSF-Seite und Komponente, Renderer bzw. Handler. Die Komponente liefert die eigentliche Funktionalität und nutzt einen

Renderer für das Rendering (abhängig vom Kontext, z. B. HTML-Code). Eine spezielle Java-Klasse (Tag Handler genannt) kann als »Behandlungsklasse« eines Tags definiert werden und wird beim Aufbau des Komponentenbaums aufgerufen. Dafür muss die Klasse javax.faces.view.facelets.TagHandler erweitert werden. Wird kein Tag Handler definiert, erzeugt Facelets einen Standard Tag Handler. Weiterhin ist es möglich einen Tag Handler speziell für die Komponente zu erstellen. Hierfür muss die Klasse javax.faces.view.facelets.ComponentHandler erweitert werden.

5.5 Expression Language *

In der Abb. 5.4-1 ist der Aufbau einer JSF-Komponente illustriert. In der TLD muss bei einem Tag entweder eine Komponente oder ein Tag Handler eingetragen werden. Zusätzlich kann zu einer Komponente ein konkreter Renderer konfiguriert werden. Facelets - nutzt die TLD

nutzt

TLD - definiert ein Tag

- nutzt das in der TLD definierte Tag

Komponente verweist auf

verweist auf

- bietet u.a. eine encodeBegin() Methode an nutzt Renderer - rendert das Ergebnis (z.B. HTML-Code)

verweist auf erzeugt automatisch

Java-Klasse (Tag Handler)

Abb. 5.4-1: JSF Komponente.

In JSF werden die Tags nur als Platzhalter in den JSF-Seiten genutzt. Man spricht beispielsweise davon, ein Tag in die JSF-Seite einzufügen. Spricht man hingegen über das Generieren von HTML, bezieht man sich in der Regel auf die Komponente bzw. den verwendeten Renderer. Verglichen mit einfachen Tags, verfügen Komponenten über mehr Möglichkeiten. Im weiteren Verlauf wird näher auf diese Möglichkeiten eingegangen.

5.5

Expression Language *

Die EL ( Expression Language) ermöglicht eine Kommunikation zwischen den Views und der Geschäftslogik. Die EL ( Expression Language) wurde initial mit der JSTL unter der Bezeichnung SPEL ( Simplest Possible Expression Language) eingeführt. Seit JSP 2.0 können EL-Ausdrücke auch außerhalb von JSTL-Tags verwendet werden, um Java-Code vom JSP-Anteil zu separieren. Mit der Einführung von JSF wurde ebenfalls eine entsprechende EL spezifiziert, die im JSF-Kontext genutzt werden konnte. Eine eigene EL für JSF war notwendig, da die ELAusdrücke bei JSP direkt evaluiert werden (beim Parsen der Seite). Der Lebenszyklus von JSF-Anwendungen ist komplizierter und daher können die EL-Ausdrücke auch in einer späteren Phase evaluiert werden. Derzeit wird die Expression Language in der Version 3.0 angeboten. Diese ist grundsätzlich unabhängig von JSP und JSF, verbindet jedoch die »JSP-EL« und die »JSF-EL«. Die Spezifikation kann unter [Orac13] eingesehen werden.

33

34

5 JavaServer Faces *

Evaluierung Die EL unterscheidet immediate und deferred Evaluierung. Immediate

Bei immediate wird ein bestimmter Ausdruck evaluiert und zurückgegeben, sobald die Seite das erste Mal gerendert wurde. Dieses Verhalten ist bereits aus JSP bekannt. Diese Evaluationsart wird mittels »$« festgelegt und kann nur lesend auf bestimmte Attributwerte zugreifen.

Deferred

Im Gegensatz dazu, kann bei deferred die Evaluierung in einer anderen Phase des Lebenszyklus durchgeführt werden. Diese Evaluierungsart wird mittels »#« festgelegt und kann sowohl lesend als auch schreibend auf Attributwerte zugreifen. Zusätzlich können auch Methoden ausgewertet werden. Dies ist bei immediate nicht möglich.

Hinweis

Bei der bevorzugten Seitendeklarationssprache Facelets wird nicht zwischen imme-

diate und deferred unterschieden. Die Ausdrücke mit »$« werden genauso behandelt wie »#«. Es handelt sich immer um deferred Evaluierung.

Ausdrücke value und method Es werden zwei Arten von Ausdrücken unterschieden: value expression und method expression. Value expression

Bei value expression kann weiterhin zwischen rvalue (Daten nur lesen) und lvalue (Daten lesen und schreiben) unterschieden werden. Es handelt sich dabei um einen Zugriff auf Attributwerte. Ausdrücke mit »$« sind immer rvalue, während Ausdrücke mit »#«

rvalue oder lvalue sein können (abhängig von der darunterliegenden Technologie). Hinweis

Method expression

Facelets behandelt Ausdrücke mit »$« genauso wie »#«. Der Zugriff kann lesend oder schreibend erfolgen. Bei method expression werden Methoden aufgerufen. Diese Methoden müssen öffentlich sein und können einen Rückgabewert liefern. Ebenfalls können Parameter an diese Methoden übergeben werden.

Hinweis

Da eine Methode in unterschiedlichen Phasen des Lebenszyklus aufgerufen werden kann, handelt es sich dabei immer um deferred Evaluierung. Die Ausdrücke werden also mit »#« eingeleitet.

Beispiel

Im folgenden Beispiel werden die Arten value expression und method expression

DemoEL

gezeigt. Es wird ein Objekt der Klasse jsf.beans.Ausgabe unter dem Namen ausgabe (@Named("ausgabe")) als Managed Bean im Gültigkeitsbereich einer Sitzung (@SessionScoped) zur Verfügung gestellt. Managed Beans sind Objekte, die vom Anwen-

dungsserver verwaltet werden. Die Objekte werden dabei automatisch erzeugt. Die Konfiguration solcher Objekte wird im Kapitel »Managed Beans«, S. 35, beschrieben. Die Konfigurationsdatei web.xml wird an dieser Stelle nicht dargestellt. Ausgabe.java

@Named("ausgabe") @SessionScoped public class Ausgabe implements Serializable { private String param;

35

5.6 Managed Beans *

private boolean auswahl = false; // Getter und Setter ... public String ausgeben(String parameter) { this.auswahl = true; return parameter + " und " + this.param; } }







index.xhtml

Beim ersten Aufruf der Seite wird das folgende Ergebnis dargestellt:

Erklärung

Ausgabe 2: parameter und null Ausgabe 3:

Die erste Ausgabe wird nicht generiert, da zunächst geprüft wird, ob ausgabe.auswahl!=false ist. Dies ist beim ersten Aufruf der Seite nicht gegeben, da dieser

Wert initial mit false belegt wird. Die zweite Ausgabe ruft die Methode String ausgeben(String parameter) auf und übergibt den Parameterwert parameter. Innerhalb der Methode wird auswahl=true gesetzt und anschließend wird der konkatenierte Wert parameter, »und« und der Wert von param ausgegeben. Da noch kein Wert für param

vorhanden ist, wird null ausgegeben. Die dritte Ausgabe wird gerendert, da beim vorherigen Methodenaufruf der Wert auswahl=true gesetzt wurde. Die Bedingung ist somit erfüllt. Beim Senden des Werts eingabe wird folgende Ausgabe erzeugt: Ausgabe 1: eingabe Ausgabe 2: parameter und eingabe Ausgabe 3: eingabe

Die Bedingungen für die erste und dritte Ausgabe sind erfüllt und es ist nun ein Wert für param vorhanden.

5.6

Managed Beans *

Managed Beans sind Objekte, die vom Anwendungsserver verwaltet werden. Die Objekte werden dabei automatisch erzeugt. Weiterhin lassen sich die Properties einer Managed Bean automatisch füllen. Das Konzept der Managed Beans wird in den folgenden Kapiteln beschrieben.

36

5 JavaServer Faces *

 

»Einführung in Managed Beans«, S. 36 »Contexts and Dependency Injection«, S. 37



»Contexts«, S. 39

 

»Dependency Injection«, S. 44 »Weiterführende Konfiguration«, S. 48

5.6.1 Einführung in Managed Beans * Managed Beans sind Objekte, die von Anwendungsserver automatisch erzeugt und in einem bestimmten Gültigkeitsbereich zur Verfügung gestellt werden. Konfiguriert werden sie über Annotationen in der jeweiligen Managed Bean -Klasse. Einführung

Um Objekt-Zugriffe in JSF-Seiten unter einem bestimmten Namen (z. B. benutzer) zu ermöglichen, müssen diese erzeugt und in einem Gültigkeitsbereich zur Verfügung gestellt werden. Die Verwaltung von Objekten sollte ab JSF 2.3 ausschließlich über CDI ( Contexts and Dependency Injection) erfolgen (siehe »Contexts and Dependency Injection«, S. 37). Bei der Verwendung von Angaben wie #{benutzer.name} ist auf den ersten Blick nicht ersichtlich, um welchen Typ es sich handelt und in welchem Gültigkeitsbereich das Objekt vorhanden ist. Diese Informationen müssen zusätzlich hinterlegt werden. Dies geschieht über Annotationen in der zugrundeliegenden Managed Bean -Klasse.

Was ist eine Managed Bean ?

Bei Objekten, die automatisch durch einen Container (im Kontext von JSF ist dies CDI) erzeugt und in einem Gültigkeitsbereich gespeichert werden, spricht man von Mana-

ged Beans . Managed Bean Klasse

Eine Managed Bean ist ein »kontextuelles Exemplar« einer Managed Bean -Klasse. Eine Managed Bean -Klasse wird zur Umsetzung für Zustandsspeicherungen und/oder zur Umsetzung von Logik verwendet. Der zugrundeliegende Container (in diesem Fall der CDI-Container ) ist verantwortlich für die Erstellung und die Zerstörung des Objekts, sowie der Einordnung in den konkreten Kontext. Kontextuelle Exemplare können in andere Objekte desselben Kontextes »injiziert« werden.

Managed Bean -Klassen sollten folgende Voraussetzungen erfüllen: 

Beispiel Benutzer.java

Definition eines eindeutigen Namens



Definition eines Gültigkeitsbereichs

 

Implementierung von Serializable Verwendung eines parameterlosen Konstruktors

Eine annotierte Managed Bean -Klasse wird im Folgenden gezeigt. @Named("benutzer") @SessionScoped public class Benutzer implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

37

5.6 Managed Beans *

Nicht alle oben beschriebenen Voraussetzungen müssen explizit erfüllt werden: 

Erklärung

In der Klasse Benutzer.java wird die Annotation @Named("benutzer") verwendet, um ein Objekt dieses Typs unter den Namen benutzer zur Verfügung zu stellen. Das Auslassen der Namensbezeichnung durch Verwendung @Named würde in diesem Fall dasselbe Ergebnis liefern: Es wird der Klassenname mit einem kleinen Anfangsbuchstaben als Standardname vergeben. Weitere Konstellationen im Kontext der Namensgebung sind möglich. Für weitere Informationen wird auf [ReHa17] verwiesen.



Die Annotation @SessionScoped legt fest, dass das Objekt im Kontext einer Sitzung gespeichert wird. Weitere Möglichkeiten zur Festlegung der Verfügbarkeiten werden in »Contexts«, S. 39, beschrieben.





Die Schnittstelle Serializable als ein reines Marker-Interface muss bei Gültigkeitsbereichen eingesetzt werden, in denen Objekte serialisiert und deserialisiert werden. Bei Nichtvorhandensein eines parameterlosen Konstruktors wird dieser implizit hinzugefügt. Aus diesem Grund ist die Angabe keine Pflicht. Wird jedoch ein parametrisierter Konstruktor umgesetzt, findet das implizite Hinzufügen nicht statt.

5.6.2

Contexts and Dependency Injection *

CDI ( Contexts and Dependency Injection) (JSR 365) ist ein Standard von Java EE ( Enterprise Edition) mit dem es möglich ist, die Verwaltung von Objekten im Anwendungsserver durchzuführen. Damit sind die verwendeten Objekte in der gesamten Anwendung verfügbar. Bevor auf CDI eingegangen werden kann, müssen zunächst ein paar Begriffe eingeführt werden. Im Kontext dieses Buches war immer von Webanwendungen die Rede. In

Java EE

Unternehmensanwendungen (auch Enterprise -Anwendungen) ist dies jedoch nur eine Komponente der gesamten Softwarearchitektur. Eine Softwarearchitektur beschreibt dabei die allgemeine Struktur eines Softwaresystems. Speziell im Java-Umfeld existiert die »Java EE«-Spezifikation, die eine Softwarearchitektur für Unternehmensanwendungen vorgibt. Der Anwendungsserver GlassFish der Firma Oracle ist die Referenzimplementierung dieser Spezifikation. Daneben gibt es weitere Anwendungsserver-Implementierungen wie z. B. Payara, JBoss. Die aktuelle Spezifikation »Java EE 8« wird von »GlassFish 5« umgesetzt. Die Spezifikation von CDI kann unter [ReHa17] eingesehen werden. Innerhalb der Java EE-Spezifikation sind Server- und Containerdienste die wichtigsten Elemente. Ein Container beschreibt eine logische Komponente, die eine spezielle Aufgabe erfüllt und mit anderen Containern kommunizieren kann. Java EE definiert die folgenden Container :    

Applet Container : Verwaltung von Applets Application Client Container : Verwaltung von Anwendungen, die auf dem Client ausgeführt werden Web Container : Verwaltung von Webanwendungen Enterprise JavaBeans Container : Verwaltung von EJBs

Container

38

5 JavaServer Faces *

EJBs

EJBs implementieren die Geschäftslogik, also die eigentliche »Aufgabe« der Unternehmensanwendung.

Schichten

Die verfügbaren Container werden auf verschiedene Schichten aufgeteilt: Application

Client Container und Applet Container werden auf die Client-Schicht verteilt, der Web Container wird auf die Web-Schicht verteilt und der Enterprise JavaBeans Container wird auf die Geschäftslogik-Schicht verteilt. Diese logischen Schichten wiederum lassen sich auf physikalische Ziele verteilen: Die Client-Schicht läuft auf dem Client-Rechner und die Web-Schicht sowie die Geschäftslogik-Schicht laufen auf dem Anwendungsserver (Java EE-Server). Zusätzlich ist noch eine Persistenz-Schicht vorhanden, die auf einem Datenbankserver läuft. Eine vereinfachte Darstellung der Schichten wird in Abb. 5.6-1 gezeigt.

Client-Schicht

Anwendung

Webbrowser

Web-Schicht

Clientrechner

JSF Anwendungsserver

Geschäftslogik-Schicht

Enterprise Beans

Persistenz-Schicht

Datenbank

Datenbankserver

Abb. 5.6-1: Schichten einer Anwendung.

Eine Clientanwendung greift direkt auf die Geschäftslogik-Schicht zu, während ein Webbrowser zunächst auf die Web-Schicht zugreifen muss. Ausschließlich über die Geschäftslogik-Schicht kann auf die Persistenz-Schicht zugegriffen werden, um vorhandene Objekte zu persistieren (zu speichern). Für weitere Informationen zu softwaretechnischen Infrastrukturen wird auf [Balz11] verwiesen. Eine detaillierte Übersicht der Java EE 8-Spezifikation wird in [Orac17] gegeben. Contexts

Mit Contexts wird der Gültigkeitsbereich der Managed Beans bestimmt. Die verfügbaren Gültigkeitsbereiche werden im Kapitel »Contexts«, S. 39, beschrieben.

Dependency Injection

Der Begriff Dependency Injection beschreibt ein Verfahren, bei dem einem Objekt von außen ein anderes Objekt zur Verfügung gestellt wird. Das Objekt muss die Erzeugung des benötigten Objekts nicht selbst realisieren. Man gewinnt Flexibilität und eine lose Kopplung. Die Objekte werden an unterschiedlicher Stelle verwaltet. Bei CDI-Managed Beans ist dies der Dienst Context and Dependency Injection, der in allen Containern zur Verfügung steht. Eine nähere Betrachtung wird im Kapitel »Dependency Injection«, S. 44, gegeben.

39

5.6 Managed Beans *

CDI-Managed Beans werden durch den Anwendungsserver verwaltet. Die so erzeugten Objekte können über die Expression Language in der Webanwendung verwendet

Managed Beans

werden, zusätzlich aber auch im gesamten Anwendungsserver. Es handelt sich bei den erzeugten Objekten nicht direkt um Exemplare der Managed Bean -Klassen, da vom Anwendungsserver sogenannte Proxies generiert werden, die den Zugriff auf die eigentlichen Objekte kapseln (sog. Proxy Pattern ). Durch den Einsatz von Proxies können nichtfunktionale Anforderungen, z. B. Sicherheitsaspekte, leichter umgesetzt werden. Die erstellten Objekte können zudem in beliebigen anderen Artefakten (z. B. Filtern, Lis-

tenern ) eingesetzt werden. Im weiteren Verlauf des Buches sind mit Managed Beans immer CDI-Managed Beans gemeint. Enterprise JavaBeans werden im EJB Container verwaltet und besitzen im Gegensatz zu CDI-Managed Beans erweiterte Funktionalitäten wie Transaktionen oder Sicherheitsaspekte.

5.6.3

Vergleich CDI und EJB

Contexts *

Es existieren verschiedene Gültigkeitsbereiche (auch: Scopes ), die festlegen, wie lange ein Objekt existiert und von wo darauf zugegriffen werden kann. Innerhalb einer Webanwendung lassen sich Objekte erzeugen und anschließend speichern. Jedoch stellt sich die Frage, wo genau diese Objekte gespeichert werden. Bei einer gewöhnlichen Java-Anwendung wird ein Objekt in einer Eigenschaft eines anderen Objektes oder in einer statischen Eigenschaft gespeichert. Bei einer Weban-

Motivation

wendung wird ein Objekt jedoch von mehreren Benutzern genutzt. Es muss demnach festgelegt werden, an welcher Stelle die Objekte zur Verfügung gestellt und wie lange diese verfügbar bleiben sollen. Genau diese Festlegung bezeichnet man als Gültigkeitsbereich. Ein Benutzername z. B. ist klar benutzerbezogen. Aber auch Informationen wie eine Suchanfrage, ein Suchergebnis oder ein gerade zu bearbeitender Datensatz sind be-

Möglichkeiten zur Objektspeicherung

nutzerbezogene Informationen, die nur für einen bestimmten Benutzer verfügbar sein sollen. Es existieren je nach Erfordernis verschiedene Möglichkeiten, ein Objekt zu nutzen: 

RequestScoped: Das Objekt kann genutzt werden, solange die aktuelle Benutzeran-

forderung ( Request ) bearbeitet wird. Also auch, wenn der Request an eine andere Seite weitergeleitet wird. Der Request endet erst, nachdem der Response vollständig geschrieben wurde und die Verarbeitung beendet ist. 

ViewScoped: Für jede Ansicht (auch View ) wird ein neues Objekt erzeugt. Erst wenn

die Ansicht gewechselt wird, ist das Objekt nicht mehr verfügbar. Hierbei handelt es sich um einen von JSF definierten Gültigkeitsbereich.  

SessionScoped: Das Objekt kann genutzt werden, solange die Session besteht. ApplicationScoped: Das Objekt kann innerhalb der gesamten Anwendung (und da-

mit von allen Benutzern) genutzt werden. Eine Reihe weiterer Gültigkeitsbereiche sind vorhanden (Auszug): 

ConversationScoped: Ein Objekt ist für die Dauer einer »Konversation« verfügbar.



TransactionScoped: Objekte dieses Gültigkeitsbereichs sind im Rahmen einer aktiven

Transaktion verfügbar.

Gültigkeitsbereiche

40

5 JavaServer Faces *



FlowScoped: Ein Flow beschreibt eine Unit of Work. Vom Anwendungsserver verwaltete Managed Beans sind nur in den jeweiligen Flows gültig und können von

anderen Flows nicht zugegriffen werden. Auf das Thema wird im Kapitel »Faces Flows«, S. 237, näher eingegangen. Zusätzlich sind noch »Pseudo-Gültigkeitsbereiche« vorhanden: 

Dependent: Hierbei ist der Lebenszyklus eines Objektes von dem Lebenszyklus eines

übergeordneten Objektes (CDI-Managed Bean ) abhängig. 

Singleton: Ein Singleton repräsentiert ein »einzelnes« Objekt einer Klasse.

Statt selbst festzulegen, wie das Objekt verwendet werden soll, wird lediglich ein abstrakter Gültigkeitsbereich festgelegt. Die technischen Details, wo und wie das Objekt genau gespeichert wird, werden vor dem Entwickler verborgen. Hier wird also von der zugrundeliegenden Technik abstrahiert. Die Gültigkeitsbereiche können benutzerabhängig oder -unabhängig sein. In der Abb. 5.6-2 wird dieser Zusammenhang noch einmal illustriert. benutzerunabhängig

benutzerbezogen

{

{

Gültigkeitsbereich

View Scoped

Application Scoped

Request Scoped

Session Scoped

Abb. 5.6-2: Matrix der Gültigkeitsbereiche und ihre Zugriffsmöglichkeiten. Zugriff

Alle verwalteten Objekte können programmatisch über eine Klasse namens BeanManager ermittelt werden. Die Implementierung ist für einen Webentwickler jedoch zunächst uninteressant. An welcher Stelle das Objekt tatsächlich gespeichert wird, spielt keine

Rolle. Es ist nur wichtig, wann und von wo aus auf das Objekt zugegriffen werden kann. Lebensdauer

Durch die Speicherung des Objekts in einem bestimmten Gültigkeitsbereich ergeben sich jedoch nicht nur Zugriffsbeschränkungen (ein Benutzer kann z. B. nur auf Objekte seiner eigenen Session zugreifen), sondern auch die »Lebensdauer« des Objekts wird durch den Gültigkeitsbereich bestimmt, in dem es gespeichert wird. So ist ein Objekt, welches mit RequestScoped annotiert wurde, nur so lange zugreifbar, wie der Request aktuell bearbeitet wird. Die Abb. 5.6-3 vergleicht die Lebensdauern der Gültigkeitsbereiche. Durch das Gültigkeitsprinzip muss die technische Realisierung nicht betrachtet werden. Das vereinfacht die Entwicklung und verbessert die Verständlichkeit der Anwendung, da auf einen Blick zu sehen ist, in welchem Gültigkeitsbereich sich das Objekt befindet.

Beispiel

Zur Veranschaulichung der verschiedenen Gültigkeitsbereiche wird eine Klasse Benutzer erstellt, die entsprechend der Abb. 5.6-4 implementiert wird (Programm DemoGueltigkeitsbereiche).

41

5.6 Managed Beans *

ApplicationScoped SessionScoped ViewScoped RequestScoped Lebensdauer Abb. 5.6-3: Gültigkeitsbereich: Vergleich der Lebensdauern.

Benutzer - alter: int + Benutzer() + Benutzer(int alter) + getAlter(): int + erhoeheAlter(): void

Abb. 5.6-4: Gültigkeitsbereiche: Beispiel.

Die Klasse Benutzer sieht folgendermaßen aus: public class Benutzer implements Serializable { private int alter; public Benutzer() { } public Benutzer(int alter) { System.out.println("Benutzer mit Alter ’" + alter + "’ erstellt"); this.alter = alter; } public int getAlter() { System.out.println("Methode getAlter"); return this.alter; } public void erhoeheAlter() { System.out.println("Methode erhoeheAlter"); this.alter++; } }

Über eine »Hilfsklasse« BenutzerBean werden Objekte der Klasse Benutzer in den folgenden Gültigkeitsbereichen zur Verfügung gestellt:    

RequestScoped : Zugriff über benutzerBean.request. Initialisierung mit Alter 10. ViewScoped : Zugriff über benutzerBean.view. Initialisierung mit Alter 20. SessionScoped : Zugriff über benutzerBean.session. Initialisierung mit Alter 30. ApplicationScoped : Zugriff über benutzerBean.application. Initialisierung mit Alter 40.

Zur besseren Unterscheidung werden unterschiedliche Altersangaben bei der Objekterzeugung festgelegt. Die verwendeten Konzepte zur Erzeugung werden im Kapitel »Weiterführende Konfiguration«, S. 48, beschrieben.

Benutzer.java

42

5 JavaServer Faces *

Für jeden Gültigkeitsbereich wird eine JSF-Seite erstellt, welche ein Formular mit einer Ausgabe (Komponente h:outputText) und einer Schaltfläche (Komponente h:commandButton) enthält. Exemplarisch wird die Datei indexRequest.xhtml dargestellt. Für die anderen Gültigkeitsbereiche werden analoge Seiten erstellt. indexRequest .xhtml





Die verschiedenen Gültigkeitsbereiche werden miteinander verglichen. Die Objekterzeugungen sind jeweils hervorgehoben. Vergleich Request Scoped mit View Scoped

Im Folgenden soll zunächst der Gültigkeitsbereich RequestScoped mit dem Gültigkeitsbereich ViewScoped verglichen werden. Hierzu werden die Aufrufe der JSFSeite indexRequest.xhtml mit den Aufrufen der JSF-Seite indexView.xhtml verglichen (vgl. Tab. 5.6-1).

Request Scoped

View Scoped

Seite laden

1. Benutzer mit Alter ’10’ erstellt 2. Methode getAlter

1. Benutzer mit Alter ’20’ erstellt 2. Methode getAlter

Schaltfläche gedrückt

1. Benutzer mit Alter ’10’ erstellt 2. Methode erhoeheAlter 3. Methode getAlter

1. Methode erhoeheAlter 2. Methode getAlter

Tab. 5.6-1: Vergleich RequestScoped mit ViewScoped.

Erklärung

Beim Laden der Seiten wird ein neues Objekt erzeugt und anschließend die Methode getAlter() ausgeführt. Beim erneuten Drücken der Schaltfläche wird bei Verwendung RequestScoped ein neues Objekt erzeugt, während bei Verwendung von ViewScoped das innerhalb der Ansicht vorhandene Objekt genutzt wird.

Hinweis

Bei einem Forward wird der Request übernommen, nicht aber bei einem Redirect . Bei einem Forward wird derselbe HTTP-Request verwendet, während bei einem

Redirect ein neuer HTTP-Request erzeugt wird. Vergleich View Scoped mit Session Scoped

Nun wird der Gültigkeitsbereich ViewScoped mit dem Gültigkeitsbereich SessionS-

coped verglichen. Es werden die JSF-Seiten indexView.xhtml und indexSession.xhtml verglichen (vgl. Tab. 5.6-2).

43

5.6 Managed Beans *

ViewScoped

SessionScoped

Seite laden

1. Benutzer mit Alter ’20’ erstellt 2. Methode getAlter

1. Benutzer mit Alter ’30’ erstellt 2. Methode getAlter

Schaltfläche gedrückt

1. Methode erhoeheAlter 2. Methode getAlter

1. Methode erhoeheAlter 2. Methode getAlter

Seite erneut laden

1. Benutzer mit Alter ’20’ erstellt 2. Methode getAlter

1. Methode getAlter

Tab. 5.6-2: Vergleich ViewScoped mit SessionScoped.

Beim ersten Laden der Seiten wird jeweils ein neues Objekt erzeugt und die Me-

Erklärung

thode getAlter() ausgeführt. Beim anschließenden Drücken der Schaltfläche werden ebenfalls in beiden Gültigkeitsbereichen die Methoden auf dem vorhandenen Objekt ausgeführt. Wird die JSF-Seite indexView.xhtml neu geladen, wird auch ein neues Objekt erzeugt, während beim erneuten Laden der JSF-Seite indexSession.xhtml das innerhalb der Sitzung vorhandene Objekt genutzt wird. Abschließend wird der Gültigkeitsbereich SessionScoped mit dem Gültigkeitsbereich ApplicationScoped verglichen. Dazu werden die Aufrufe der JSF-Seiten indexSession.xhtml und indexApplication.xhtml verglichen (vgl. Tab. 5.6-3).

SessionScoped

ApplicationScoped

Seite laden

1. Benutzer mit Alter ’30’ erstellt 2. Methode getAlter

1. Benutzer mit Alter ’40’ erstellt 2. Methode getAlter

Schaltfläche gedrückt

1. Methode erhoeheAlter 2. Methode getAlter

1. Methode erhoeheAlter 2. Methode getAlter

Seite erneut laden (>Sitzungszeit)

1. Benutzer mit Alter ’30’ erstellt 2. Methode getAlter

1. Methode getAlter

Vergleich Session Scoped mit Application Scoped

Tab. 5.6-3: Vergleich SessionScoped mit ApplicationScoped.

Wie in den anderen Gültigkeitsbereichen werden auch hier beim ersten Laden der JSF-Seiten neue Objekte erzeugt und getAlter() auf den Objekten ausgeführt. Durch anschließendes Drücken der Schaltfläche werden in beiden Gültigkeitsbereichen die Methoden auf dem vorhandenen Objekt ausgeführt. Beim erneuten Laden der Seite kommt es nun bei Verwendung von SessionScoped darauf an, ob die Sitzungszeit abgelaufen ist oder nicht. Falls die Sitzung nicht mehr gültig ist, wird beim erneuten Aufruf der Seite ein neues Objekt erzeugt. Bei Verwendung von ApplicationScoped ist das Objekt unabhängig von der vergangenen Zeit weiterhin vorhanden.

Erklärung

44

5 JavaServer Faces *

5.6.4 Dependency Injection * CDI bietet mit noch mehr Möglichkeiten, als es auf den ersten Blick vermuten lässt. So lassen sich Abhängigkeiten einer Managed Bean automatisch auflösen. Man spricht dann hierbei von Dependency Injection. Im Java-Umfeld werden Objekte üblicherweise mittels new initialisiert. Beim Einsatz von CDI überlassen wir dem CDI-Container die Erstellung und Verwaltung von Objekten. Diese Objekte lassen sich direkt in Properties anderer Objekte typsicher »injizieren«. Das Konzept wird als Dependency Injection bezeichnet. Diese Aufgabe wird ebenfalls vom CDI-Container übernommen. Beispiel 1

Das folgende Beispiel erklärt eine Anwendungsmöglichkeit von Dependency Injection (DemoDependencyInjection). Angenommen die Klasse Caller soll einen Logger verwenden, um Log -Ausgaben zu erstellen. Es gibt zwei Implementierungen dieses Loggers : Einmal ein Logger, der Ausgaben auf die normale Konsole tätigt (»out«) und einmal ein Logger, der auf die Fehlerkonsole schreibt (»err«). Beide konkreten Klassen implementieren die Schnittstelle Logger. Die Klassen heißen einmal LoggerOut und einmal LoggerErr. Die Klasse Caller kann also beide Implementierungen nutzen, je nachdem, ob auf der normalen

oder der Fehlerkonsole geloggt werden soll. Die Abb. 5.6-5 illustriert das Beispiel als UML-Diagramm.

Caller + getLogger(): Logger + setLogger(Logger): void + toString(): String

Logger

+logger 1

+ log(String): void

LoggerErr + log(String): void

Abb. 5.6-5: Managed Bean -Beispiel.

Beispiel 1a

Ein Codebeispiel sieht wie folgt aus: Schnittstelle: public interface Logger extends Serializable { public void log(String logtext); }

Implementierung mit Standardausgabe: public class LoggerOut implements Logger { public void log(String logtext) { System.out.println(logtext); } }

LoggerOut + log(String): void

45

5.6 Managed Beans *

Implementierung mit Fehlerausgabe: public class LoggerErr implements Logger { public void log(String logtext) { System.err.println(logtext); } }

Die toString()-Methode der Klasse Caller wird wie folgt implementiert: public String toString() { logger.log("toString wurde aufgerufen"); return "ein Caller-Objekt"; }

Nun wäre es möglich, dass in der Klasse Caller eine Methode implementiert wird, die, je nachdem welche Art von Logger benötigt wird, LoggerOut oder LoggerErr erzeugt. Das kann beispielsweise im Konstruktor passieren. Ein entsprechender Kon-

Realisierung ohne Dependency Injection

struktor, der LoggerOut verwendet, sähe wie folgt aus: public Caller() { logger = new LoggerOut(); }

Diese Art von Code sorgt jedoch für eine starke Abhängigkeit zur Klasse LoggerOut bzw. LoggerErr. Um dieses Problem zu umgehen, wird Dependency Injection verwendet. Bei Nutzung von Dependency Injection wird die Klasse Caller nicht selbst Objekte der Klasse LoggerOut oder LoggerErr erzeugen, sondern einfach ein Property logger vom Typ

Realisierung mit Dependency Injection

Logger bereitstellen, welches automatisch mit einem Objekt von LoggerOut oder LoggerErr gefüllt wird. Die Klasse Caller kann also einfach davon ausgehen, dass das

entsprechende Property korrekt gefüllt ist und das entsprechende Objekt zum Loggen verwenden. Natürlich geschieht das Füllen der Properties nicht automatisch. Es muss irgendwo definiert werden, welche Properties einer Managed Bean automatisch gefüllt werden sollen. Diese Konfiguration wird über Annotationen realisiert. Das Füllen der Properties ist eng verknüpft mit der Managed Bean -Konfiguration. Da die Properties nicht notwendigerweise in einem expliziten Gültigkeitsbereich vorhan-

Voraussetzungen für Dependency Injection Konfiguration

den sein müssen (sie werden ja Managed Beans zugeordnet und sind daher über diese zugreifbar), ist es hier möglich, die Annotation Dependent zu setzen. Das hierdurch injizierte Objekte ist abhängig von der Managed Bean und wird bei der Erstellung der

Managed Bean erstellt und bei der Zerstörung auch wieder gelöscht. Die Konfiguration der Klasse Caller wird nachfolgend gezeigt. @Named @SessionScoped public class Caller implements Serializable { @Inject private Logger logger; ... }

Beispiel 1b

46

5 JavaServer Faces *

Durch die Annotation @Inject wird festgelegt, dass CDI ein Objekt vom Typ Logger in das Property logger injizieren soll. Die Klasse LoggerErr muss ebenfalls erweitert werden. @Dependent public class LoggerErr implements Logger { public void log(String logtext) { System.err.println(logtext); } }

Mittels der Annotation @Dependent wird festgelegt, dass die Verfügbarkeit der automatisch erstellten Objekte von der zugrundeliegenden Managed Bean abhängen. Bei der Erstellung der Managed Bean wird auch ein Objekt der Klasse LoggerErr erstellt und bei der Zerstörung auch wieder gelöscht.

Erweiterte Mechanismen Der Mechanismus ist nicht darauf beschränkt, nur einfache Properties zu füllen. Neben einfachen Properties lassen sich auch Listen über diesen Mechanismus injizieren. Beispiel 2

Das vorherige Beispiel wird aufgegriffen und dahingehend erweitert, dass in der Klasse Caller eine Collection von Loggern gespeichert werden soll. Alle verfügbaren

Logger sollen bei Aufruf der Methode toString() ausgeführt werden (DemoDependencyInjectionErweitert). Die Klasse Caller wird nachfolgend dargestellt. @Named @SessionScoped public class Caller implements Serializable { @Inject @Any private Instance logger; ... public String toString() { for(Logger l : logger) l.log("toString wurde aufgerufen"); return "ein Caller-Objekt"; } }

Durch die Annotation von @Any werden alle vorhandenen Objekte in das Property logger injiziert. Beim Aufruf der Methode toString() werden alle Logger aufgerufen.

Hierfür ist es notwendig, dass beide Implementierungen LoggerErr und LoggerOut der Schnittstelle Logger annotiert werden. @Dependent public class LoggerErr implements Logger { public void log(String logtext) { System.err.println(logtext); } } @Dependent public class LoggerOut implements Logger { public void log(String logtext) {

47

5.6 Managed Beans *

System.out.println(logtext); } }

Die Annotation @Dependent legt fest, dass die injizierten Objekte abhängig von der

Managed Bean sind. Die nachfolgende Konsolenausgabe wird erzeugt: INFORMATION: toString wurde aufgerufen SCHWERWIEGEND: toString wurde aufgerufen

Es ist ersichtlich, dass beide Logger verwendet werden. Neben den proprietären Fachkonzeptklassen können auch auch JSF-Objekte injiziert werden. In diesem Beispiel soll der Faces Context in ein Property injiziert werden (DemoDependencyInjectionErweitert). Der Faces Context enthält sowohl alle Informationen zu

dem Request, der die JSF-Verarbeitung ausgelöst hat, als auch zu dem Response, der durch die JSF-Verarbeitung erstellt wird. Außerdem ermöglicht er die direkte Kommunikation mit dem JSF-Framework. Weitergehende Informationen werden im Kapitel »Rund um die JSF-Anwendung«, S. 175, gegeben. In der Klasse InjectionCaller soll der Faces Context automatisch injiziert werden. Die Klasse wird nachfolgend dargestellt. @Named @SessionScoped public class InjectionCaller implements Serializable { @Inject private FacesContext facesContext; public String toString() { for(Map.Entry e : facesContext.getExternalContext(). getInitParameterMap().entrySet()) System.out.println( e.getKey() + " : " + e.getValue()); return "ein Injection-Caller-Objekt"; } }

Zusätzlich hierzu wurde die JSF-Seite um einen Aufruf dieses Objekts erweitert. ...




...

Die nachfolgende Konsolenausgabe wird erzeugt: com.sun.faces.forceLoadConfiguration : true javax.faces.PROJECT_STAGE : Development

Der zweite Wert ist aus der Konfigurationsdatei web.xml und legt fest, dass das Projekt sich im Entwicklungsstadium befindet.

Beispiel 3

48

5 JavaServer Faces *

5.6.5 Weiterführende Konfiguration ** Neben den bereits eingeführten Konzepten von CDI können weiterführende Konfigurationen eingesetzt werden.

Konfigurationsarten Bei der Verwendung von CDI werden CDI-Managed Beans in sogenannten Bean Archi-

ves gesucht. Dabei ist ein Bean Archive ein Modul, dass entsprechende Managed Beans enthält. Es werden zwei Arten von Bean Archives unterschieden: explizit und implizit. Falls ein Modul (z. B. eine Webanwendung) ein Bean Archive enthält, dann können Managed Beans verwaltet und injiziert werden. Module ohne Bean Archive werden nicht nach Managed Beans durchsucht. Diese Vorgehensweise erhöht die Performanz. Soll zusätzlich die Möglichkeit bestehen, mittels EL-Ausdrücken auf die Managed Bean zuzugreifen, kann die Annotation @Named(..) verwendet werden. »Explizit«

Ein explizites Bean Archive zeichnet sich dadurch aus, dass eine Konfigurationsdatei beans.xml vorhanden ist. Bei Webanwendungen wird diese Datei im Verzeichnis »WEB-

INF« und bei Clientanwendungen im Verzeichnis »META-INF« gespeichert. Falls keine Interceptors (Komponenten, die Methodenaufrufe abfangen und eigene Funktionen ausführen), Decorators (funktionale Erweiterungen von Klassen) oder Alternatives (alternative Auswahl; wird im weiteren Verlauf des Kapitels beschrieben) in der Konfigurationsdatei festgelegt werden sollen, kann diese auch leer sein. Zur Verdeutlichung wird das bekannte Beispiel 1 zur Erstellung von Log -Ausgaben aus »Dependency Injection«, S. 44, im Programm DemoCDI1explizit mit explizit konfigurierten Managed Beans realisiert. Beispiel 1

Im Verzeichnis WEB-INF ist eine inhaltslose Datei beans.xml vorhanden, um die explizite Konfiguration festzulegen.

index.xhtml

Die JSF-Datei beinhaltet lediglich eine Ausgabe des Objektes caller.



LoggerOut.java

Die Klasse LoggerOut gibt einen Text in der Konsole aus. public class LoggerOut { public void log(String logtext) { System.out.println(logtext); } }

Caller.java

In der Klasse Caller wird deklarativ festgelegt, dass ein Objekt der Klasse LoggerOut in das Property logger »injiziert« werden soll. Dies wird vom Anwendungsserver übernommen. @Named("caller") public class Caller { @Inject private LoggerOut logger; public LoggerOut getLogger() { return logger; } public void setLogger(LoggerOut logger) {

49

5.6 Managed Beans *

this.logger = logger; } @Override public String toString() { logger.log("toString wurde aufgerufen"); return "ein Caller-Objekt"; } }

Beim Aufruf der JSF-Seite wird lediglich ein Caller-Objekt ausgegeben. Da kein Gültigkeitsbereich angegeben wurde, wird an dieser Stelle Dependent festgelegt.

Erklärung

Ein implizites Bean Archive wird mittels Annotationen festgelegt. Dazu wird in allen

»Implizit«

Klassen nach passenden Annotationen (z. B. @SessionScoped) gesucht. Auf diese Weise kann auf die Konfigurationsdatei beans.xml verzichtet werden. Die zu verwaltenden Klassen (inkl. aller abhängigen Klassen) müssen eine entsprechende Annotation besitzen. Das Beispiel 1 wird im Programm DemoCDI2implizit mit impliziter Konfiguration realisiert. Statt der Konfigurationsdatei beans.xml werden nun Annotationen verwendet, um

Beispiel 2

das Modul (diese Webanwendung) als Bean Archive festzulegen. Die JSF-Datei index.xhtml unterscheidet sich nicht vom vorherigen Beispiel. Die Klassen Caller und LoggerOut unterscheiden sich nur dahingehend, dass explizit ein Gültigkeitsbereich

angegeben wird. @Dependent public class LoggerOut { ... }

LoggerOut.java

@Named("caller") @Dependent public class Caller { ... }

Caller.java

Beim Aufruf der JSF-Seite wird ebenfalls ein Caller-Objekt ausgegeben. Es wird der

Erklärung

Gültigkeitsbereich Dependent festgelegt. Die Verwendung bestimmter Annotationen (z. B. @SessionScoped) bedingt, dass Serializable implementiert wird. Dies liegt daran, dass sowohl @SessionScoped, @ApplicationScoped, @FlowScoped (siehe »Faces Flows«, S. 237) als auch @ConversationScoped sogenannte »passivating scopes « sind. Die Objekte in diesen Gültigkeitsbereichen

können »passiviert« werden, das heißt, dass sie temporär von einem aktiven Status der Anwendung in einen sekundären Speicher transferiert werden können. Bei der Verwendung von @ApplicationScoped ist die Implementierung der Schnittstelle Serializable beispielsweise nicht notwendig. Bei der Verwendung von Dependent hängt es vom Kontext der Klasse ab, ob Serializable implementiert werden muss oder nicht.

Würde die Klasse Caller mit @SessionScoped annotiert und die Klasse LoggerOut mit @Dependent, dann müssten beide Klassen Serializable implementieren.

Hinweis

50

5 JavaServer Faces *

Mehrere Implementierungen mit Qualifier Oftmals ist es so, dass mehrere Implementierungen vorhanden sind. Um diese zu unterschieden können sogenannte Qualifier verwendet werden. Diese Qualifier werden in den Klassen als Kennzeichnung verwendet. Das Beispiel 1 wird im Programm DemoCDI3Qualifier mit verschiedenen Loggern implementiert. Beispiel 3

Es wird explizite Konfiguration verwendet (Konfigurationsdatei beans.xml ist hier leer) und die JSF-Seite wird unverändert übernommen. Es werden zunächst zwei

Qualifier QualifierError und QualifierStandard implementiert. Qualifier Error.java

Qualifier Standard.java

@Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface QualifierError { } @Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface QualifierStandard { }

Eine Schnittstelle für die zwei verfügbaren Log -Mechanismen wird erstellt. Logger.java

public interface Logger { public void log(String logtext); }

Die konkreten Logger -Klassen geben einen Text in der Konsole aus, einmal als normale Ausgabe (System.out.println(...)) und einmal als Fehlerausgabe (System.err.println(...)). LoggerOut.java

@QualifierStandard public class LoggerOut implements Logger { public void log(String logtext) { System.out.println(logtext); } }

LoggerErr.java

@QualifierError public class LoggerErr implements Logger { public void log(String logtext) { System.err.println(logtext); } }

Die Klasse Caller besitzt nun zwei Properties für die jeweiligen Log -Typen. Caller.java

@Named("caller") public class Caller { @Inject @QualifierStandard private Logger loggerStandard; @Inject @QualifierError private Logger loggerError; // Getter und Setter ... @Override public String toString() { loggerStandard.log(

51

5.6 Managed Beans *

"[loggerStandard] toString wurde aufgerufen"); loggerError.log( "[loggerError] toString wurde aufgerufen"); return "eine Caller Instanz"; } }

In das Property loggerStandard wird ein Objekt der Klasse LoggerOut und in das Pro-

Erklärung

perty loggerError wird ein Objekt der Klasse LoggerErr injiziert.

Mehrere Implementierungen mit Alternative Die Verwendung von Qualifier hat den entscheidenden Nachteil, dass die jeweilige Einstellung statisch im Quelltext als Annotation vorhanden ist. Soll die Konfiguration ohne die Änderung des Quelltextes realisiert werden, bietet sich eine Konfiguration mit @Alternative an. Dazu werden die alternativen Klassen mit der Annotation @Alternative

gekennzeichnet und über die Konfigurationsdatei beans.xml wird die konkrete Klasse definiert. Die Klasse Caller sowie die JSF-Datei index.xhtml unterscheiden sich qualitativ nicht

Beispiel 4

vom Beispiel 1. Die Klassen LoggerOut und LoggerErr werden mit der Annotation @Alternative gekennzeichnet (Programm DemoCDI4Alternative). public interface Logger { public void log(String logtext); }

Logger.java

@Alternative public class LoggerOut implements Logger { ... }

LoggerOut.java

@Alternative public class LoggerErr implements Logger { ... }

LoggerErr.java

Die Konfigurationsdatei beans.xml ist nun nicht mehr inhaltslos, sondern definiert die zu verwendende Alternative.

jsf.beans.LoggerErr

beans.xml

In diesem Fall wird ein Objekt der Klasse LoggerErr in das entsprechende Properties der Klasse Caller injiziert. Als Gültigkeitsbereich wird Dependent festgelegt.

Erklärung

52

5 JavaServer Faces *

Hinweis

Durch die Angabe bean-discovery-mode="all" werden alle vorhandenen Klassen als

Managed Beans zur Verfügung gestellt (explizites Bean Archive ). Eine weitere Möglichkeit wäre bean-discovery-mode="annotated" (implizites Bean Archive ). In diesem Fall ist für jede Klasse noch der Gültigkeitsbereich anzugeben. Ohne Verwendung einer Konfigurationsdatei beans.xml wird implizit bean-discovery-mode="annotated" festgelegt.

»Produzenten«-Methoden Oftmals reicht eine statische Konfiguration der konkreten Implementierung nicht aus. In diesen Fällen muss ein Weg gefunden werden, die Injizierung der Objekte zur Laufzeit zu verändern. Dies kann mit sogenannten Producer -Methoden realisiert werden. Dieses Vorgehen wird im folgenden Beispiel DemoCDI5Produzent gezeigt. Beispiel 5

Es soll eine JSF-Seite erstellt werden, die zur Laufzeit den Logger einstellen kann. Die GUI wird in Abb. 5.6-6 gezeigt.

Abb. 5.6-6: GUI des Beispiels CDI-Produzent.

Abhängig von der Auswahl Err oder Out soll der entsprechende Logger eingestellt werden und eine Ausgabe erfolgen. In diesem Beispiel wird implizite Konfiguration genutzt (ohne beans.xml). Die Artefakte Logger, LoggerOut, LoggerErr können übernommen werden und sind ohne Annotationen vorhanden. Es wird die Definition eines Qualifier benötigt: Qualifier Logger.java

@Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface QualifierLogger { }

In der Klasse Caller wird dieser Qualifier für die Injizierung verwendet. Caller.java

@Named("caller") @SessionScoped public class Caller implements Serializable { @Inject @QualifierLogger private Logger logger; ... }

Hier ist erkennbar, dass nur der allgemeine Qualifier verwendet wird. Es ist also zur Entwurfszeit noch nicht festgelegt, welcher konkrete Logger verwendet wird. Die Festlegung eines konkreten Loggers wird von der Klasse ProducerLogger durchgeführt. Dazu wird die Auswahl des Benutzers in dem Property loggerType gespeichert.

53

5.6 Managed Beans *

@Named("producerLogger") @ApplicationScoped public class ProducerLogger { private final static int ERR = 1; private final static int OUT = 2; private int loggerType = ERR;

Producer Logger.java

@Produces @QualifierLogger @RequestScoped public Logger getConcreteLogger() { switch(loggerType){ case ERR: return new LoggerErr(); case OUT: return new LoggerOut(); default: return null; } } // Getter und Setter ... }

Die aktuelle Auswahl des Benutzers wird als Zahl im Property loggerType gespeichert.

Erklärung

Die Methode getConcreteLogger() ist die »Produzent«-Methode des Qualifiers QualifierLogger. Das bedeutet, dass jedes Mal, wenn ein entsprechendes Objekt injiziert werden soll, diese Produzent-Methode festlegt, welches konkrete Objekt erstellt werden soll und dies in Abhängigkeit von der aktuellen Benutzerauswahl. Die JSF-Seite sieht folgendermaßen aus:







Sobald die Schaltfläche Auswählen betätigt wurde, wird die aktuelle Auswahl übernommen. Die Ausgabekomponente ausgabe führt anschließend die toString()-Methode der Managed Bean caller aus.

index.xhtml

54

5 JavaServer Faces *

5.7 Annotationen vs. Konfigurationsdatei ** Annotationen sind »Metainformationen«, die verwendet werden können, um semantische Informationen im Quelltext festzulegen. Mit Annotationen können Bereiche der Konfigurationsdatei faces-config.xml ersetzt werden, in dem die Konfigurationsinformationen direkt in die Java-Klassen geschrieben werden. Beispiel

Im folgenden Beispiel soll ein Meldungsfenster bei Änderungen in Eingabefeldern dargestellt werden (Programm DemoAnnotationKonfiguration). Zur Umsetzung wird das clientseitige Verhalten zweier benutzerdefinierten Tags verändert. Die Tags werden in der Datei custom.taglib.xml definiert.

custom.taglib.xml

http://jcp.org/custom

annotation

jsf.beans.ConfirmAnnotation

configuration

jsf.beans.ConfirmConfiguration



Die Tags werden im Namespace http://jcp.de/custom registriert. Eine genaue Beschreibung von Tags wird im Kapitel »Eigene Tags«, S. 225, gegeben. An dieser Stelle werden lediglich wichtige Teile zur Unterscheidung von Annotationen und Konfigurationen beschrieben. Das Verhalten dieser Tags soll in den Klassen ConfirmationAnnotation (für den Tag annotation) bzw. ConfirmationConfiguration (für den Tag configuration) implementiert

werden. Confirmation Annotation.java

Confirmation Configuration.java

@FacesBehavior("jsf.beans.ConfirmAnnotation") public class ConfirmationAnnotation extends ClientBehaviorBase { @Override public String getScript( ClientBehaviorContext behaviorContext) { return "return confirm(’Annotation: Bestätigen?’)"; } } public class ConfirmationConfiguration extends ClientBehaviorBase { @Override public String getScript( ClientBehaviorContext behaviorContext) { return "return confirm(’Konfiguration: Bestätigen?’)"; } }

55

5.7 Annotationen vs. Konfigurationsdatei **

In der Klasse ConfirmationAnnotation reicht die Annotation @FacesBehavior zur Registrierung aus. In der Klasse ConfirmationConfiguration sind keine Registrierungsinformationen vorhanden, daher muss dies in der Konfigurationsdatei faces-config.xml stattfinden. faces-config.xml

jsf.beans.ConfirmConfiguration

jsf.beans.ConfirmationConfiguration



In

der

JSF-Seite

werden

die

beiden

Tags

im

Namespace

xmlns:c="http://jcp.org/custom" zur Verfügung gestellt.

...










index.xhtml

Ein exemplarischer Aufruf wird in Abb. 5.7-1 dargestellt.

Abb. 5.7-1: Beispiel: Annotation vs. Konfiguration.

Bei Änderungen des Eingabewerts »Annotation« (z. B. durch Hinzufügen eines Leerzeichens) wird die Meldung Annotation: Bestätigen angezeigt. Im weiteren Verlauf des Buches werden sowohl Annotationen als auch die Konfigurationsdatei faces-config.xml verwendet.

Verwendung

56

5 JavaServer Faces *

5.8 Bindings ** Der Begriff Binding bezeichnet die Verbindung einer Komponente zu Proper-

ties (Value Binding oder Instance Binding ) oder Methoden ( Method Binding). Bindings werden genutzt, um Komponenten mit Properties oder Methoden einer Managed Bean zu verknüpfen. Value Binding verknüpft dabei den Wert einer Komponente mit einem Property einer Managed Bean. Instance Binding verknüpft die »gesamte« Komponente mit einem Property einer Managed Bean. Method Binding verknüpft eine »Aktion« einer Komponente mit einer Methode einer Managed Bean. Value Bindings

Value Bindings werden unter anderem dazu genutzt, um Properties von Komponenten mit Managed Bean-Properties zu verknüpfen. Ändert sich der Wert der Komponente (beispielsweise der Komponente h:inputText), wird auch das Property des Value Bindings entsprechend geändert. Aktualisierungen werden im Kontext von JSF in der Phase Update Model Values durchgeführt (siehe »Der JSF-Lebenszyklus«, S. 165). Der Einsatz von Value Binding ermöglicht eine größere Kontrolle der Komponentenattribute durch den Autor der JSF-Seite. Die Managed Bean besitzt keine Abhängigkeiten zu der JSF-API. Dies ermöglicht eine bessere Aufteilung von Präsentations- und Fachlogik. Konvertierungen können durch JSF durchgeführt werden, ohne dass diese manuell implementiert werden müssen.

Instance Bindings

Instance Bindings werden genutzt, um Komponenten mit Managed Bean-Properties zu verknüpfen. In diesem Fall besitzt die Managed Bean vollständigen Zugriff auf die Komponente. Üblicherweise wird Instance Binding genutzt, wenn die Attribute einer Komponente dynamisch zur Laufzeit geändert werden müssen, beispielsweise das Attribut rendered. Die Managed Bean kann die Attribute einer Komponente programmiertechnisch verändern. Objekte von Komponenten können in der Managed Bean erstellt werden.

Method Bindings

Method Bindings werden genutzt, um »Aktionen« von Komponenten mit Managed Bean -Methoden zu verknüpfen. Üblicherweise wird Method Binding für Schaltflächen, z. B. »Speichern«, »Löschen«, genutzt. Eine Methode einer Managed Bean kann einer »Aktion« einer Komponente zugeordnet werden.

Unterschied

Die Abb. 5.8-1 verdeutlicht die Unterschiede zwischen Value Binding, Instance Binding und Method Binding : JSF-Browser Value Binding Titel

JSF speichern

abbrechen

Instance Binding

Benutzer - titel: String - input: HTMLInputText + ausgeben(): void

Method Binding Abb. 5.8-1: Ablauf: Bindings.

57

5.8 Bindings **

Im folgenden Beispiel sollen die Unterschiede zwischen Value Binding, Instance Bin-

ding und Method Binding verdeutlicht werden. Es werden Managed Beans für die Klassen die Klassen jsf.beans.ValueBinding und jsf.beans.InstanceBinding im Gültigkeitsbereich einer Sitzung eingebunden. Die Konfigurationsdatei web.xml wird an dieser Stelle nicht dargestellt.

Beispiel DemoBindings

@Named @SessionScoped public class ValueBinding implements Serializable { private String text; public ValueBinding() { } public String getText() { return text; } public void setText(String text) { this.text = text; } public void ausgeben() { System.out.println("ValueBinding: " + this.text); } }

ValueBinding.java

@Named @SessionScoped public class InstanceBinding implements Serializable { private HtmlInputText input = new HtmlInputText(); public InstanceBinding() { this.input.setTitle("PLZ"); this.input.setMaxlength(5); this.input.setValue("12345"); } public HtmlInputText getInput() { return this.input; } public void setInput(HtmlInputText input){ this.input = input; } public void ausgeben() { System.out.println("InstanceBinding: " + this.input.getValue()); } }

InstanceBinding.java

Value Binding



Instance Binding



index.xhtml

In der Datei index.xhtml wird für das Absenden ein h:commandButton verwendet und die eigentliche »Aktion« mittels Method Binding an die jeweilige Managed Bean -

Erklärung

Methode verknüpft. In diesem Fall handelt es sich um die Methode ausgeben().

58

5 JavaServer Faces *

Die Klasse ValueBinding besitzt lediglich ein Property text zum Speichern eines gewöhnlichen Textwerts. Die Klasse InstanceBinding besitzt ein Property vom Typ HtmlInputText. Damit ist es möglich, die Methoden setTitle(..) (Erzeugung eines Titels), setMaxlength(..) (Festlegung der maximal zulässigen Zeichenanzahl) sowie setValue(..) (in-

itialer Wert) aufzurufen und die anzuzeigende Komponente somit aus der Managed Bean heraus zu konfigurieren. Das Herstellen des Bindings (der Daten) unterscheidet sich lediglich durch die Nutzung der Attribute value bzw. binding.

Fallstudie Blog -Anwendung – Übersicht *

6

Die folgende Fallstudie wird eingesetzt, um die jeweiligen Themen der nachfolgenden Kapitel praktisch zu vertiefen. Es handelt sich hierbei um eine Blog Anwendung. Zunächst werden die benötigten Klassen für die »Geschäftslogik« sowie die grundlegenden Konfigurationsdateien erstellt. Ein Blog ist eine Art Tagebuch. Es können beliebige Texteinträge erstellt werden, die

Was ist ein Blog ?

jeweils mit Titel, Datum und einer Kategorie versehen werden. Diese können dann untereinander dargestellt werden, sodass eine Art Tagebuch entsteht. Um die Anwendung nicht zu kompliziert werden zu lassen, ist die »Geschäftslogik«

Funktionsweise

(hier existiert statt Geschäftslogik ausschließlich Persistenzlogik) für diese Beispielanwendung (FallstudieUebersicht) bereits implementiert und wird daher nicht näher erläutert. Die zur Verfügung gestellten Klassen bieten die Möglichkeit, Texteinträge mit einem Titel, einem Text, einer Kategorie und einem Datum in einem definierbaren Verzeichnis zu speichern. Alternativ wird das Home -Verzeichnis des aktuellen Benutzers zur Speicherung genutzt. Eine Mehrbenutzer-Nutzung ist nicht vorgesehen. Die Abb. 6.0-1 zeigt, wie die fertige Software in etwa aussehen könnte. JSF-Browser ID 4

Datum 17.07.2018

Kategorie Allgemein

3

07.07.2018

Allgemein

2

13.04.2018

Unterhaltung

1

19.02.2018

Allgemein

Titel und Text Lesestatus Bin bereits beim 3. Kapitel. Kapitel 2 Die Grundlagen habe ich jetzt verstanden. Beginn Das Buch ist hoffentlich spannend. Buch erhalten Ich habe gerade das JSF-Buch erhalten.

Abb. 6.0-1: Fallstudie: Liste der Blog -Einträge.

Der zentrale Anwendungsfall der Anwendung ist es, einen Blog -Eintrag zu erzeugen. Der Benutzer soll die Möglichkeit haben, die Angaben für Titel, Datum, Kategorie und

Blog -Eintrag erzeugen

Text zu erfassen. Diese Angaben werden dann als Blog -Eintrag über die zur Verfügung gestellten Klassen gespeichert (Abb. 6.0-2). Die Klassen, die das Abspeichern und Laden der Einträge ermöglichen, werden bereits zur Verfügung gestellt und müssen daher nicht selbst programmiert werden. In diesem Kapitel werden die Klassen vorgestellt, die für diesen und die nächsten Anwendungsfälle benötigt werden. Die Klassen werden in der Abb. 6.0-3 als UML-Diagramm dargestellt. Die zentrale Klasse BlogManager stellt die Persistenz zur Verfügung und die Klasse BlogEntry stellt einen Eintrag in dem Blog dar.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_6

Klassen

60

6 Fallstudie Blog -Anwendung – Übersicht *

JSF-Browser Titel Datum Kategorie Text

speichern

abbrechen

Abb. 6.0-2: Fallstudie: Ablauf Blogeintrag -Eingabe.

BlogEntry

BlogManager

- id: long - title: String - text: String - category: String - date: String

+ saveEntry(BlogEntry): void + deleteEntry(BlogEntry): void + getAllEntries(): List - getFilename(): String - saveDb(List): void - initDb(): void

Abb. 6.0-3: API der Fallstudie in UML-Notation.

In der Tab. 6.0-1 werden die Properties der Klasse BlogEntry aufgelistet. Property

Bedeutung

id

Eine eindeutige Zahl. Diese wird durch den BlogManager – beim erstmaligen Speichern – vergeben und braucht nicht explizit gesetzt werden.

title

Der Titel des Blog -Eintrags.

text

Der Inhalt des Blog -Eintrags.

category

Die Kategorie des Blog -Eintrags.

date

Ein Datum, auf das sich der Blog -Eintrag bezieht.

Tab. 6.0-1: Properties von BlogEntry.

Die Klasse BlogManager stellt die folgenden Methoden zur Verfügung: Methoden von



BlogManager

public void saveEntry(BlogEntry entry):

Speichert einen neuen Eintrag. 

public void deleteEntry(BlogEntry entry):



public List getAllEntries():

Löscht einen vorhandenen Eintrag. Liefert alle gespeicherten Einträge zurück.

61

6 Fallstudie Blog -Anwendung – Übersicht *



private String getFilename():

Gibt den Pfad zur Persistenzdatei zurück. Ist vom Benutzer ein Pfad in der web.xml definiert, wird dieser genutzt. Andernfalls wird das Home -Verzeichnis des aktuellen Benutzers zur Speicherung genutzt. 

private void saveDb(List list):



private void initDb():

Persistiert die vorhandenen Blog -Einträge. Initialisierung die Datenbank. Standardmäßig wird der Pfad c:\blog.db für die Speicherung der Blog -Einträge verwendet. In der Konfigurationsdatei web.xml kann ein benutzerdefinierter Speicherpfad festgelegt werden:

blogdbpath c:\blog.db

Eigener Speicherpfad

web.xml

Ein Objekt der Klasse BlogManager wird als Managed Bean im Gültigkeitsbereich einer Sitzung erzeugt: @Named @SessionScoped public class BlogManager implements Serializable { ... }

BlogManager.java

Übersicht Die Abb. 6.0-4 zeigt eine Übersicht der vorhandenen Programme. Programm Templating Programm Internationalisierung Programm Eingabemaske Programm Übersicht

Programm Dateiupload Programm Tabellen

Programm Navigation Programm Konverter Programm Validierung Programm Auswahllisten Programm Ressourcen

Abb. 6.0-4: Fallstudie: Übersicht der verschiedenen Programme.

62

6 Fallstudie Blog -Anwendung – Übersicht *

Aufbauend auf dem Programm FallstudieÜbersicht werden die Programme FallstudieEingabemaske, FallstudieDateiupload und FallstudieTabellen implementiert. Das Programm FallstudieEingabemaske dient als Grundlage für die Programme FallstudieTemplating, FallstudieInternationalisierung, FallstudieNavigation, FallstudieKonverter, FallstudieValidierung, FallstudieAuswahllisten und FallstudieRessourcen. Die Unterschiede werden bei der Beschreibung

der jeweiligen Fallstudie erläutert. Im Programm FallstudieGesamt werden alle Inhalte zu einer Anwendung zusammengefasst.

7

Grundlegende JSF-Komponenten *

Ein sehr wichtiger Punkt bei der Entwicklung von Webanwendungen ist die Seitende-

Einleitung

klarationssprache. Im Fall von JSF sind dies die Facelets . In Facelets können verschiedene Tag -Bibliotheken verwendet werden. Die JavaServer Faces HTML Tag Library enthält

Tags für alle UIComponent-Objekte. Dabei handelt es sich um die Komponenten, die den HTML-Code erzeugen. Im Folgenden geht es um »grundlegende« JSF-Komponenten. Das sind einfache Textausgaben genauso wie Schaltflächen. All diese Komponenten verlangen keine genauere Kenntnis von JSF, da sie maßgeblich wie bei HTML funktionieren. Jedoch verwendet man bei JSF nicht die herkömmlichen HTML-Tags, sondern spezielle JSF-Tags. Anhand dieser einfachen Komponenten werden die ersten JSF-spezifischen Abläufe und einige grundlegende Interna erläutert. Nachfolgend werden die Kapitel, in denen die grundlegenden JSF-Komponenten erläutert werden, aufgelistet: 

»Box: Komponentenhierarchie«, S. 64



»h:outputText«, S. 66

 

»h:inputText, h:inputSecret, h:inputTextarea«, S. 68 »h:commandButton, h:commandLink«, S. 72



»Die Komponenten im Zusammenspiel«, S. 75



»Fallstudie Blog-Anwendung – Eingabemaske«, S. 76

Die Tab. 7.0-1 zeigt eine kurze Übersicht über die Komponenten aus den genannten Kapiteln. Tag

Beschreibung

h:outputText

Gibt einen Text aus.

h:inputText

Stellt ein einzeiliges Textfeld dar.

h:inputSecret

Stellt ein einzeiliges Textfeld zur Passworteingabe dar.

h:inputTextarea

Stellt ein mehrzeiliges Textfeld dar.

h:commandButton

Stellt eine Schaltfläche dar.

h:commandLink

Stellt einen Verweis ( Link ) dar.

Tab. 7.0-1: Übersicht über die Komponenten.

Die Tags werden im Folgenden jeweils kurz beschrieben, anschließend folgt eine Liste mit den Attributen, die das jeweilige Tag unterstützt, und danach Beispiele mit Erklärungen, in denen jeweils der Aufruf der Komponente, die Darstellung im Browser und das generierte HTML gezeigt werden.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_7

Grundlegende JSFKomponenten

64

7 Grundlegende JSF-Komponenten *

7.1 Box: Komponentenhierarchie * Die vorhandenen Komponentenklassen sind hierarchisch angeordnet. In Abb. 7.1-1 sind die in diesem Buch relevanten Komponentenklassen mit den festgelegten PropertyKeys aufgeführt.

UIComponent ~ PropertyKeys : enum = rendered, attributes, bindings, rendererType, systemEventListeners, behaviours, passThroughAttributes

UIComponentBase - id: String

UICommand ~ PropertyKeys : enum = value, immediate, methodBindingActionListener, actionExpression

UIForm ~ PropertyKeys : enum = prepenId, lastId, submitted UIParameter

UISelectItem ~ PropertyKeys : enum = itemDescription, itemDisabled, itemEscaped, itemLabel, itemValue, value, noSelectionOption

UISelectItems

~ PropertyKeys : enum = name, value, disable UIViewRoot ~ PropertyKeys : enum = viewId, locale, lastId, beforePhase, afterPhase, phaseListeners, resourceLibraryContracts, renderKitId

~ PropertyKeys : enum = value UIColumn UIOutput ~ PropertyKeys : enum = value, converter

UIInput ~ PropertyKeys : enum = localValueSet, required, requiredMessage, valid, validatorMessage, immediate, converterMessage

UISelectOne

UISelectMany

Abb. 7.1-1: Komponentenhierarchie.

Die verwendeten Elemente in einer JSF-Seite werden in einem Komponentenbaum abgebildet (siehe »Komponentenbaum«, S. 161). Speziell im Umfeld von HTML sind weitere Komponenten vorhanden, die in Abb. 7.1-2 dargestellt sind.

7.1 Box: Komponentenhierarchie *

UIOutput HtmlHead

UIOutput HtmlBody

~ PropertyKeys : enum = dir, lang, xmlns

~ PropertyKeys : enum = dir, lang, on*, onload, onunload, role, style, styleClass, title, xmlns

UIColumn HtmlColumn ~ PropertyKeys : enum = footerClass, headerClass, rowHeader

UIInput HtmlInputSecret ~ PropertyKeys : enum = accesskey, alt, autocomplete, dir, disabled, maxlength, on*, onblur, onchange, onfocus, onselect, readonly, redisplay, role, style, styleClass, tabindex, title UIInput HtmlInputTextarea ~ PropertyKeys : enum = accesskey, cols, dir, disabled, label, lang, maxlength, on*, onblur, onchange, onfocus, onselect, readonly, role, rows, style, styleClass, tabindex, title

UIForm HtmlForm ~ PropertyKeys : enum = accept, dir, enctype, acceptcharset, lang, on*, onreset, onsubmit, role, style, styleClass, target, title

UICommand HtmlCommandButton ~ PropertyKeys : enum = accesskey, alt, dir, disabled, image, label, lang, on*, onblur, onchange, onfocus, onselect, readonly, role, style, styleClass, tabindex, title, type UIInput HtmlInputHidden UIInput HtmlInputFile ~ PropertyKeys : enum = accesskey, alt, autocomplete, dir, disabled, label, lang, maxlength, on*, onblur, onchange, onfocus, onselect, readonly, role, size, style, styleclass, tabindex, title

UICommand HtmlCommandLink ~ PropertyKeys : enum = accesskey, charset, coords, dir, disabled, hreflang, lang, on*, onblur, onfocus, rel, rev, role, shape, style, styleClass, tabindex, target, title, type UIInput HtmlInputText ~ PropertyKeys : enum = accesskey, alt, autocomplete, dir, disabled, label, lang, maxlength, on*, onblur, onchange, onfocus, onselect, readonly, role, size, style, styleClass, tabindex, title UIOutput HtmlOutput ~ PropertyKeys : enum = dir, escape, lang, role, style, styleclass, title

Legende: - on* = onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup Abb. 7.1-2: Komponentenhierarchie (HTML).

Zwischen den Komponenten gilt die Beziehung, wie in Abb. 7.1-3 dargestellt.

UIComponent

UIComponentBase

* children

parent

Abb. 7.1-3: Komponentenbeziehung.

Einer Komponente können weitere Komponenten als »Kind«-Elemente hinzugefügt werden.

65

66

7 Grundlegende JSF-Komponenten *

7.2 h:outputText * Das h:outputText-Tag ermöglicht die Ausgabe von beliebigem Text auf der JSFSeite. Das h:outputText-Tag rendert im einfachsten Fall kein spezielles HTML, sondern nur den Text. Damit ist er einer der einfachsten Tags von JSF. Die Tab. 7.2-1 zeigt die (zunächst) wichtigsten Attribute dieses Tags. Attribut

Bedeutung

escape

Gibt an, ob HTML- oder XML-Steuerzeichen besonders dargestellt werden (escaped , z. B. Zeichen wie »0) retval = MessageFormat.format(retval, args); return retval; } public String getText(String key, Object arg1, Object arg2) { return getText(key, new Object[] {arg1, arg2}); } public String getText(String key) { return getText(key, (Object[])null); } public String getBaseName() { return baseName; } public void setBaseName(String baseName) { this.baseName = baseName; } }

Der Zugriff auf das Objekt erfolgt über var3. Das Attribut basename erhält den Wert jsf.i18n.var3.messages. Es sind drei Methoden getText() vorhanden, je nachdem, ob

Parameter für die Ersetzungen übergeben werden oder nicht. Diese Variante kann eingesetzt werden, wenn spezielle Anforderungen bestehen, z. B. Realisierung eines Datenbankzugriffs oder Zusammenfügen von Werten. Die JSF-Seite kann nun die Methoden aufrufen: index.xhtml





Der Einsatz von h:outputFormat ist hier nicht notwendig, da die Managed Bean die Ersetzung vornimmt. Es wird folgende Ausgabe erzeugt: Vorname Nachname Bitte geben Sie eine Zahl zwischen 1 und 99 ein

89

9.2 Fallstudie Blog -Anwendung – Internationalisierung **

9.2

Fallstudie Blog -Anwendung – Internationalisierung **

Die Fallstudie soll internationalisiert werden. Dazu werden die Internationalisierungsmechanismen von JSF genutzt, die bereits in Java vorhanden sind. Die Eingabemaske der Fallstudie (addEntry.xhtml), bestehend aus fünf JSF-Elementen, soll internationalisiert werden. In diesem Fall sind drei Texte zu internationalisieren: 

Zwei Beschreibungen der Textfelder Titel und Text (h:outputText)



Text der Schaltfläche speichern (h:commandButton)

GUI

Implementierung FallstudieInternationalisierung Nachfolgend werden die Änderungen im Vergleich zum Programm FallstudieEingabemaske dargestellt. Zunächst müssen Property -Dateien für die jeweiligen Sprachen angelegt werden, die die Anwendung unterstützen soll. Die drei zu internationalisierenden Texte werden über die Bezeichner title_label, text_label und save_button realisiert. Zunächst die Realisie-

Property Dateien

rung der deutschen Bezeichner: title_label=Titel text_label=Text save_button=speichern

AddEntry Messages_de. properties

Eine Property -Datei für die englische Sprache sieht dann wie folgt aus: title_label=Title text_label=Text save_button=save

AddEntry Messages_en. properties

Die beiden Property -Dateien sollten in ein für die Internationalisierung vorgesehenes Unterpaket namens i18n (für Internationalisierung) gelegt werden. Hier heißt das vollqualifizierte Paket jsf.blog.i18n. Das Auslesen der Werte wird über das Objekt msg ermöglicht. Dazu wird die Konfigurationsdatei faces-config.xml angepasst:

jsf.blog.i18n.AddEntryMessages

msg

en en de

Im Bereich resource-bundle wird festgelegt, dass die Werte der Property -Dateien über das Objekt msg gelesen werden können. Dies ist durch die hier festgelegte Konfiguration in der gesamten Webanwendung möglich. Der Bereich locale-config legt fest, dass JSF auf die Browser-Einstellung reagieren und die entsprechend eingestellte Sprache verwenden soll. Es werden die beiden Sprachen Deutsch und Englisch unterstützt.

faces-config.xml

90

9 Internationalisierung *

Falls keine dieser Sprachen im Browser des Benutzers eingestellt ist, wird standardmäßig Englisch verwendet. Im nächsten Schritt muss die addEntry.xhtml-Datei entsprechend angepasst werden, damit die Texte aus den Property -Dateien auch verwendet werden. Dazu wird die Textausgabe so abgeändert, dass die Einträge aus den Property -Dateien verwendet werden. addEntry.xhtml







Die Beschreibungen werden über das Objekt msg geholt. Es werden die Werte title_label, text_label und save_button ausgelesen.

Managed Bean Objekte

Die beteiligten Managed Bean -Objekte unterscheiden sich nicht von dem Programm FallstudieEingabemaske. Über interne Mechanismen des JSF-Frameworks kann von jeder

JSF-Seite aus auf die Inhalte der Property -Datei über #{msg} zugegriffen werden.

10

Navigation *

Webanwendungen bestehen in der Regel nicht nur aus einer Seite. Die folgenden Kapitel erläutern die Möglichkeiten zur Realisierung von Navigation im Kontext von JSF:  

»JSF-Navigation«, S. 91 »Fallstudie Blog-Anwendung – Navigation«, S. 96

10.1 JSF-Navigation ** JSF bietet über den Navigation Rules -Mechanismus die Möglichkeit, zwischen mehreren Seiten zu navigieren. Häufig ist es notwendig, eine Anwendung mit mehreren Seiten zu realisieren. In den meisten Fällen dürfte eine Realisierung mit nur einer Seite gar nicht infrage kommen, beispielsweise bei der Eingabe von Datensätzen. Es wird unterschieden zwischen »benutzerdefinierter« und »impliziter« Navigation.

Benutzerdefinierte Navigation Üblicherweise werden Navigationsregeln (auch: Navigation Rules ) festgelegt. Diese legen fest, unter welchen Bedingungen von welcher Seite auf welche andere Seite verwiesen wird. Zum Beispiel von der Seite index.xhtml zur Seite speichernOK.xhtml. Die Bedingungen werden durch sogenannte Outcomes in der faces-config.xml beschrieben (Programm DemoNavigation). Nachfolgend wird eine Navigation Rule in der Datei faces-config.xml festgelegt.

Beispiel 1a

/index.xhtml

ok /speichernOK.xhtml

faces-config.xml

Diese Navigation Rule legt fest, dass von der Seite index.xhtml auf die Seite speichernOK.xhtml verzweigt wird. Jedoch nur bei dem Outcome ok.

Erklärung

Ein Outcome ist das Ergebnis (also der Rückgabewert) eines durch JSF initiierten Me-

Was ist ein Outcome ?

thodenaufrufs. Wie bereits gezeigt, werden bei JSF Schaltflächen und Verweise mittels Method Binding mit einer Aktion verknüpft. Folgendes Beispiel ist denkbar: JSF-Seite:

Methode: public String actionSpeichernOK() { return "ok"; }

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_10

Beispiel 1b

92

10 Navigation *

Erklärung

Im Gegensatz zu den bisher betrachteten Method Bindings, liefert dieses Method

Binding einen String zurück und hat nicht den Rückgabewert void. Bei diesem Rückgabewert spricht man auch von dem Outcome der Methode. Dieser Wert wird genutzt, um die passende Navigation Rule zu bestimmen. Hier wäre das Outcome immer ok und die aktuelle Seite ist index.xhtml. Daher wird JSF auf die Seite speichernOK.xhtml verzweigen. Würde die Methode einen unbekannten Wert zurückgegeben (zum Beispiel nok), dann würde JSF auf der aktuellen Seite (also index.xhtml) bleiben. Outcomes sind variabel

Natürlich ist es nicht nur möglich, immer den gleichen Wert zurück zu liefern, sondern es könnten über eine if-Abfrage verschiedene Werte zurückgeliefert werden. Es wäre denkbar, dass die Methode etwas prüft und abhängig davon ok oder nok zurückgibt. In Abhängigkeit des Outcomes der Methode und wie die Navigation Rules definiert sind, verweist JSF dann zu der einen oder zu der anderen Seite.

Beispiel 1c

Ein Beispiel, das von der Seite index.xhtml bei einem Outcome von ok auf speichernOK.xhtml und bei einem Outcome von nok auf speichernNOK.xhtml verweist, wäre

das folgende: faces-config.xml

Erklärung

/index.xhtml

ok /speichernOK.xhtml

nok /speichernNOK.xhtml

Es werden hier zwei Navigation Cases definiert. JSF wird den Fall berücksichtigen, dessen from-outcome-Attribut mit dem von der Methode gelieferten Outcome übereinstimmt. In diesem Fall also den ersten Navigation Case (speichernOK.xhtml), wenn das Outcome ok ist oder den zweiten Navigation Case (speichernNOK.xhtml), wenn das Outcome nok ist. Trifft kein Navigation Case zu (beispielsweise bei einem Out-

come test), wird JSF auf der aktuellen Seite (also hier index.xhtml) bleiben. Besonderheiten der JSF-Navigation

Zu dem Zeitpunkt, an dem der Benutzer die Seite index.xhtml anfordert, weiß JSF noch nicht, ob die folgende Seite speichernOK.xhtml oder speichernNOK.xhtml sein wird. Das entscheidet sich erst, nachdem der Formularinhalt von index.xhtml vom Benutzer zum Server übermittelt wurde, er also die Schaltfläche betätigt hat. Erst nach dem Betätigen der Schaltfläche (und dem damit verbundenen Übermitteln des Formularinhalts) wird das Method Binding ausgewertet. Prinzipiell benötigt jedoch jede FormularinhaltÜbermittlung an den Server eine Ziel-URL ( Uniform Resource Locator), an die diese Daten übermittelt werden. Da aber noch nicht klar ist, welche Seite folgen wird, verweist index.xhtml zunächst auf sich selbst, schickt also die Formulardaten wieder nach index.xhtml.

JSF wird jedoch nicht index.xhtml zurückliefern, sondern zunächst das Method Binding ausführen und entsprechend des Outcomes speichernOK.xhtml oder speichernNOK.xhtml.

93

10.1 JSF-Navigation **

Die Abb. 10.1-1 illustriert, wie das Auswerten der Navigation Rules bei JSF funktioniert. 4. Auswertung Method Binding

Managed Bean

1. Anfrage index.xhtml 2. Rückgabe index.xhtml

Client

3. Formular absenden

5. Outcome “ok“ JSF

6. Auswertung Navigationsregeln facesconfig.xml

7. Rückgabe speichernOK.xhtml

Abb. 10.1-1: Ablauf: Navigation Rule.



Schritt 1: Benutzer fordert die Seite index.xhtml an.



Schritt 2: JSF liefert die Seite index.xhtml zurück.



Schritt 3: Der Benutzer betätigt die Schaltfläche und übermittelt die Formularinhalte an JSF.



Schritt 4: JSF wertet das Method Binding (hier: #{beispielBean.actionSpeichernOK}) aus.



Schritt 5: Das Outcome des Aufrufes ist ok.



Schritt 6: JSF wertet die Navigation Rules aus, ermittelt also, welche Seite als Nächstes dargestellt wird. In diesem Fall (kommt von index.xhtml, Outcome ist ok)



Schritt 7: JSF liefert speichernOK.xhtml zurück.

wäre es speichernOK.xhtml. Dieses Vorgehen hat zur Folge, dass, obwohl speichernOK.xhtml vom Browser dargestellt wird, in der Browserleiste die URL index.xhtml angezeigt wird. Das hängt damit zusammen, dass der Browser ursprünglich index.xhtml angefordert hat (Schritt 3) und daher auch »denkt«, index.xhtml zu erhalten. Tatsächlich hat JSF aber speichernOK.xhtml zurückgeliefert (Schritt 7). Um das zu verhindern, kann JSF dem Browser in Schritt 7, statt des Inhalts, einen redirect

Das redirect-Element

senden. Der Browser fordert dann nochmals die Seite an und JSF antwortet erst dann mit dem Inhalt. Das hat zur Folge, dass die Anzeige der URL in der Browserleiste korrekt ist. Um dieses Verhalten anzuwenden, müssen die betroffenen Navigation Cases mit dem Element redirect erweitert werden. Zur Verdeutlichung werden die Seiten speichernOK_redirect.xhtml bzw. speichernNOK_redirect.xhtml verwendet. Die Navigation Rules sehen dann wie folgt aus:

Beispiel 1d

/index.xhtml

ok-redirect /speichernOK_redirect.xhtml



faces-config.xml

94

10 Navigation *

nok-redirect /speichernNOK_redirect.xhtml



Dieses Vorgehen verlangsamt das Laden der neuen Seite, da ein weiterer Request in Form des Redirects durchgeführt wird. Es empfiehlt sich also, das redirect nicht aus rein kosmetischen Gründen einzusetzen. Ein legitimer Grund wäre hingegen, wenn anzunehmen ist, dass der Benutzer ein Lesezeichen für die Seite erstellen möchte. Damit dieses dann auch auf die richtige Seite verweist, wäre der Redirect erforderlich. Einsatz von allgemeingültigen Navigation Rules

Bei dem from-view-id-Element dürfen auch Platzhaltersymbole (sogenannte Wildcards ), wie »*«, eingesetzt werden. Das ist insbesondere dann sinnvoll, wenn mehrere Seiten dieselbe Navigation Rule benötigen. Ein Beispiel wäre hier der Sprung in das Hauptmenü. Dieser wird in der Regel von mehreren Seiten aus benötigt. Falls eine Navigation Rule von allen Seiten benötigt wird, kann das from-view-id-Element auch weggelassen werden. Bevor für jede Seite eine Navigation Rule eingerichtet wird, ist der Einsatz von Platzhaltersymbolen empfehlenswert.

Beispiel 1e

Eine Navigation Rule, die von allen Seiten, die mit speichern anfangen, bei dem

Outcome loggedout zur index.xhtml navigiert, könnte wie folgt aussehen: faces-config.xml

Erklärung

/speichern*

loggedout /index.xhtml



Mit dem Element from-view-id wird hier festgelegt, dass die Seite mit speichern anfangen muss. Vorstellbar wäre auch ein Wert wie /*.xhtml; dann ist die Navigation

Rule für alle Seiten gültig. In diesem Beispiel wird ein Redirect durchgeführt, damit die Anzeige der URL in der Browser-Leiste korrekt ist. Das from-actionElement

Neben den beiden bereits besprochenen Elementen from-outcome und to-view-id gibt es noch ein weiteres Attribut, um die Navigationsregeln noch genauer zu spezifizieren. Hierbei handelt es sich um das Attribut from-action. Mit diesem Attribut wird zusätzlich zu dem Outcome und dem Ziel des Navigation Cases auch noch festgelegt, von welchem Method Binding das angegebene Outcome stammt.

Beispiel 1f faces-config.xml

In dem Beispiel wäre also folgender Navigation Case möglich:

/speichern.xhtml

#{beispielBean.speichern}

gespeichert /index.xhtml

95

10.1 JSF-Navigation **



Dieser Navigation Case wird also nur von JSF berücksichtigt, wenn sowohl das Out-

Erklärung

come gespeichert, als auch das aufgerufene Method Binding #{beispielBean.speichern} ist. In einem Navigation Case können bedingte Regeln definiert werden, die vor einem

Das if-Element

Seitenwechsel ausgewertet werden. Dies ist mit dem Element if möglich. Die folgenden bedingten Navigation Cases sind möglich:

Beispiel 1g

/bedingung.xhtml

geprueft #{beispielBean.abgeschlossen}

/bedingungAbgeschlossen.xhtml

geprueft #{!beispielBean.abgeschlossen}

/bedingungNichtAbgeschlossen.xhtml



faces-config.xml

Mit diesen Navigation Cases wird festgelegt, dass zur Seite bedingungAbgeschlos-

Erklärung

sen.xhtml navigiert wird, falls als Outcome geprueft zurückgegeben wird und das

Property abgeschlossen der Managed Bean beispielBean den Wert true besitzt. Andernfalls wird zur Seite bedingungNichtAbgeschlossen.xhtml navigiert. Die Tab. 10.1-1 zeigt die Elemente, die ein Navigation Case enthalten kann.

Element

Bedeutung

from-action

Method Binding, das das Outcome zurückliefert.

from-outcome

Outcome, das zurückgeliefert wird.

from-view-id

Gibt die View an, von der aus die Navigation Rule gültig ist.

if

Anwendung einer bedingten Regel, die vor dem Seitenwechsel ausgewertet wird.

redirect

Es wird eine Umleitung ( Redirect) zu der Zielseite geschickt.

to-view-id

JSF-Seite, die aufgerufen wird, wenn from-outcome und/oder from-action erfüllt sind.

Tab. 10.1-1: Elemente, die ein Navigation Case enthalten kann.

96

10 Navigation *

Implizite Navigation Neben der Möglichkeit, den Rückgabewert einer Methode als Outcome zu verwenden, gibt es auch die Möglichkeit, auf einen Methodenaufruf zu verzichten. Statt des Method Bindings wird dann im action-Attribut der h:commandButton-Komponente direkt das Outcome angegeben. Dabei handelt es sich um eine implizite Navigation. Beispiel 2

Es wird eine Schaltfläche mit dem Wert speichern für das Attribut action in die JSFSeite eingefügt.

Erklärung

Hier wird beim Drücken der Schaltfläche speichern keine Methode aufgerufen, sondern immer das Outcome speichern verwendet. Es wird daher versucht, die Seite speichern.xhtml aufzurufen.

10.2 Fallstudie Blog -Anwendung – Navigation ** In der Fallstudie sollen Navigationsregeln hinzugefügt werden, die abhängig vom Rückgabewert auf eine andere Seite verweisen. GUI

Auch die entwickelte Blog -Anwendung kommt nicht ohne Navigation aus. Es wäre denkbar, abhängig vom Rückgabewert eine Bestätigungs- bzw. Fehlerseite anzuzeigen, nachdem der Benutzer einen Blog -Eintrag angelegt hat. Die Bestätigungsseite enthält die ID des Blog -Eintrags selbst. Der Benutzer wird diese Seite sehen, wenn er auf die speichern-Schaltfläche auf der Erfassungsseite gedrückt hat. Die Information, die darzustellen ist, befindet sich in der Managed Bean addEntryBean. Beim Speichern des Eintrags (geschieht nach dem Drücken der speichern-Schaltfläche) wurde vom Blog Manager eine ID für den Blog -Eintrag vergeben und in diesem

gespeichert. Die Information muss jetzt nur noch auf dem Bildschirm ausgegeben werden. Im einfachsten Fall besteht die Bestätigungsseite also aus einem Text und der Angabe der Blog -Eintrag-ID: Der Eintrag wurde mit folgender ID angelegt: {ID des Blog-Eintrags}

Die Fehlerseite gibt lediglich einen Fehlertext aus: Bei dem Speichern eines Blogeintrages ist ein Fehler aufgetreten.

Implementierung FallstudieNavigation Nachfolgend werden die Änderungen im Vergleich zum Programm FallstudieEingabemaske dargestellt. Lediglich die nicht vorhanden Dateien showOK.xhtml und showError.xhtml müssen erstellt und die Konfigurationsdatei faces-config.xml muss erweitert werden. Die Datei showOK.xhtml sieht folgendermaßen aus: showOK.xhtml



10.2 Fallstudie Blog -Anwendung – Navigation **

97

Das h:form-Tag kann hier entfallen, da keine Informationen an den Server übermittelt werden. Die ID wird dem BlogEntry entnommen, der in dem Property blogEntry der

Managed Bean addEntryBean gespeichert wurde. In diesem befindet sich der zuletzt gespeicherte BlogEntry. Die Datei showError.xhtml besitzt folgende Gestalt:



showError.xhtml

Das h:form-Tag kann hier ebenfalls entfallen, da lediglich eine Fehlermeldung ausgegeben wird.

/addEntry.xhtml

ok /showOk.xhtml

nok /showError.xhtml

faces-config.xml

Von der Seite addEntry.xhtml wird bei einem Outcome ok auf die Seite showOK.xhtml verwiesen. Bei einem Outcome nok wird auf die Seite showError.xhtml verwiesen. An dieser Stelle wäre noch die Angabe des Attributs from-action möglich, welches festlegt, von welchem Method Binding (hier wäre es #{addEntryBean.actionSave}) das angegebene

Outcome stammt. Die beteiligten Managed Bean -Objekte unterscheiden sich nicht von dem Programm FallstudieEingabemaske.

Managed Bean Objekte

11

Konverter *

Benutzereingaben werden vom Browser immer als String an den Server übermittelt, unabhängig davon, ob die Eingabe eine Zahl ist oder nicht. Diese Eingaben werden von JSF in das benötigte Format konvertiert, beispielsweise in Integer. Dafür werden Konverter genutzt. Einige besonders häufig benötigte Konverter sind im Umfang von JSF enthalten: 

»JSF-Konverter«, S. 99

Konverter können auch selbst entwickelt werden: 

»Eigene Konverter«, S. 102

Auch in der Fallstudie wird ein Konverter benötigt: 

»Fallstudie Blog-Anwendung – Konverter«, S. 106

11.1 JSF-Konverter ** Benutzereingaben werden vom Browser als Text an den Server übermittelt, unabhängig davon, ob die Eingabe eine Zahl ist oder nicht. Dennoch werden diese Werte als Zahl in der Managed Bean abgelegt. JSF nutzt hierfür Konverter. Es können dafür von JSF mitgelieferte Konverter benutzt werden. Das Beispielprogramm DemoKonverter illustriert die automatische Konvertierung. Das h:messages-Tag stellt dabei auftretende Fehlermeldungen dar. Es wird in »Darstellen

von Validierungsfehlern«, S. 113, genauer erläutert. Der folgende Quellcode nutzt das h:messages-Tag.

Beispiel 1





In diesem Beispiel ist beispielBean.wert ein Property vom Typ double. Gibt der Benutzer eine Zahl in das Textfeld ein, wird diese automatisch umgewandelt und korrekt in die Managed Bean gespeichert. Gibt der Benutzer einen nicht numerischen Wert (z. B. test) ein, tritt, wie in der Abb. 11.1-1 sichtbar, eine Fehlermeldung auf.

Abb. 11.1-1: Fehlermeldung Konverter.

Die Konvertierung wird bei einigen Typen wie Integer, Double usw. automatisch vorgenommen.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_11

Erklärung

100

11 Konverter *

Auch die Validatoren (das Thema Validatoren wird im Kapitel »Validierung«, S. 109 vertieft) sind auf die Konverter angewiesen. So kann ein f:validateLongRange-Validator keinen String validieren, sondern nur einen numerischen Wert. Die Konvertierung muss also vorher stattfinden. Die Abb. 11.1-2 zeigt den Ablauf.

Schritt 1

Zahl

12 abbrechen

speichern

Schritt 2

Komponente submitted value

Schritt 3

local value

Schritt 4

Konvertierung

Validierung Managed Bean

Schritt 5

Abb. 11.1-2: Ablauf Konvertierung.

 

Schritt 1: Der Benutzer gibt eine Zahl ein und drückt die Schaltfläche speichern. Schritt 2: Der Wert (String) wird als submitted value in der Komponente gespeichert.



Schritt 3: Der Wert wird konvertiert. Der konvertierte Wert wird als local value gespeichert.



Schritt 4: Jetzt kann der Validator den Wert validieren. Er verwendet dafür den



(konvertierten) local value. Schritt 5: Der local value wird in der Managed Bean gespeichert. Falls erforderlich, wurde dieser Wert vorher validiert.

Der vom Browser übermittelte Wert wird also in der Komponente zunächst als submitted value (String) gespeichert, danach durch einen Konverter konvertiert und als konvertierter Wert als local value gespeichert (für weiterführende Informationen zu

submitted value und local value wird auf das Kapitel »Der JSF-Lebenszyklus«, S. 165 verwiesen). Die Tab. 11.1-1 zeigt Typen, die automatisch von JSF konvertiert werden können. Zusätzlich gibt es auch die Möglichkeit, Konverter explizit einer Komponente hinzuzufügen. Das ist zum Beispiel dann sinnvoll, wenn Werte konvertiert werden sollen, die nicht automatisch von JSF konvertiert werden können. Die Angabe von Beträgen in einer Währung wäre ein Beispiel. Hier wird JSF nicht automatisch konvertieren können, da nur der Betrag und nicht die Währung gespeichert werden kann (26,05 ¤ könnte nur als einfacher Zahlenwert 26,05 gespeichert werden, die Währung ist beim Betrag aber entscheidend). Für einen solchen Fall kann das universelle f:convertNumber-Tag eingesetzt werden.

11.1 JSF-Konverter **

Konverterklasse

Konvertierungsklasse

BigDecimalConverter

java.math.BigDecimal

BigIntegerConverter

java.math.BigInteger

BooleanConverter

java.lang.Boolean und boolean

ByteConverter

java.lang.Number

CharacterConverter

java.lang.Character und char

DoubleConverter

java.lang.Double und double

FloatConverter

java.lang.Float und float

IntegerConverter

java.lang.Integer und int

LongConverter

java.lang.Long und long

ShortConverter

java.lang.Short und short

101

Tab. 11.1-1: Von JSF automatisch konvertierbare Typen.

Ein Konverter wie f:convertNumber wird einfach als »Kind« an die Eingabekomponente gehängt. In dem folgenden Quellcode wird das f:convertNumber-Tag verwendet.

Beispiel 2



In dem Beispiel wird der Wert auf zwei Stellen genau (maxFractionDigits) mit der Währung (type="currency") Euro (currencySymbol) und Tausendertrennzeichen (groupingUsed) dargestellt. Es wird daher davon ausgegangen, dass der Benutzer aus-

schließlich Beträge in Euro eingibt. Eine valide Eingabe wäre somit 1,00 ¤. In der Managed Bean wird dann der Wert 1.0 gespeichert. Diese Konvertierung funktioniert auch in die andere Richtung. Ändert sich der Wert in der Managed Bean zum Beispiel auf den Wert 2.0, wird in der GUI 2,00 ¤ angezeigt. Das f:convertNumber-Tag ist jedoch nicht nur dazu geeignet, Währungsbeträge zu konvertieren. So ist es auch möglich, beliebig formatierte Zahlen zu konvertieren. Dazu kann ein Muster (auch Pattern ) verwendet werden. Eine Auswahl einiger Konfigurationsmöglichkeiten des Tags wird in der Tab. 11.1-2 aufgelistet. Neben dem oben beschriebenen f:convertNumber-Tag enthält JSF im Standardumfang zusätzlich noch ein f:convertDateTime-Tag , mit dem sich Benutzereingaben in Datumsobjekte konvertieren lassen. Das f:convertDateTime-Tag kann beispielsweise explizit verwendet werden, um ein Datum unabhängig von der beim Benutzer eingestellten Locale immer im spanischen Format anzuzeignen.

Erklärung

102

11 Konverter *

Attribut

Beschreibung

currencyCode

ISO 4217 Währungscode (nur wenn type="currency").

currencySymbol

Währungssymbol (nur wenn type="currency").

groupingUsed

Gibt an, ob der Wert mit Tausender-Trennzeichen dargestellt wird.

integerOnly

Gibt an, ob es sich um einen ganzzahligen Wert handelt.

locale

Locale, die zum Formatieren der Werte verwendet wird (z. B. es).

pattern

Ein Nummer-Pattern, das verwendet wird (z. B. ¤###, nur nutzbar wenn type="number").

type

Legt den Typ fest (number, currency oder percentage). Standard ist percentage.

Tab. 11.1-2: Attribute von f:convertNumber. Beispiel 3

In dem folgenden Quellcode wird das f:convertDateTime-Tag verwendet.



Erklärung

Die Ausgabe wird hier beispielsweise wie folgt aussehen: martes 6 de febrero de 2018 21:06:00 GMT.

Auch das hier vorgestellte f:convertDateTime-Tag bietet einige Attribute, die in der Tab. 11.1-3 dargestellt sind. Attribut

Beschreibung

dateStyle

Das Datumsformat: default, short, medium, long und full (Java-Standard).

locale

Locale, die zum Formatieren der Werte verwendet wird.

pattern

Pattern, das zum Parsen verwendet werden soll – timeStyle, dateStyle und type werden dann ignoriert – Beispiel: MM/dd/yyyy.

timeStyle

Das Zeitformat: default, short, medium, long und full (Java-Standard).

timeZone

Zeitzone – Beispiel: Europe/Berlin

type

Legt den Typ fest (date, time oder both). Standard ist date.

Tab. 11.1-3: Attribute von f:convertDateTime.

11.2 Eigene Konverter *** In vielen Fällen dürften die von JSF zur Verfügung gestellten Konverter ausreichend sein. Für spezielle Anforderungen gibt es die Möglichkeit, eigene Konverter zu entwickeln. Anforderungen an einen Konverter

Ein Konverter soll eine Zeichenkette (die Eingabe des Benutzers) in ein Objekt eines bestimmten Typs verwandeln. So wie ein Standard-Konverter beispielsweise String-Werte in Zahlen (z. B. Integer) konvertiert.

103

11.2 Eigene Konverter ***

Bei dem Zieltyp (also der Klasse, in die konvertiert werden soll) kann es sich um eine Klasse aus dem Standardumfang von Java oder um eine selbst entwickelte Klasse handeln. Der Konvertierungsprozess ist jedoch keine Einbahnstraße. Werte können aus Mana-

ged Beans gelesen und in Managed Beans geschrieben werden. Ein selbstentwickelter Konverter muss daher zwei Anforderungen erfüllen: 

Eine Zeichenkette in ein Objekt des Zieltyps verwandeln können.



Ein Objekt des Zieltyps wieder in eine Zeichenkette verwandeln können.

Durch diese Anforderungen wird offensichtlich, dass zwei Methoden benötigt werden, um die Konvertierung bewerkstelligen zu können. Eine Methode, um einen String

Aufbau eines Konverters

in das Objekt vom Zieltyp zu verwandeln, und eine Methode, die das Objekt wieder zurückverwandeln kann. JSF bietet eine Schnittstelle ( Interface) an, die von der implementierenden KonverterKlasse genau diese zwei Methoden verlangt. Es ist also nicht vorgesehen, die Konvertierungsmethoden in der Managed Bean -Klasse zu implementieren. Stattdessen besteht der Konverter aus einer eigenen Klasse, die die beschriebene Konverter-Schnittstelle (javax.faces.convert.Converter) implementiert. Die Abb. 11.2-1 zeigt eine UML-Darstellung dieser Konverter-Schnittstelle.

javax.faces.convert.Converter + getAsObject(FacesContext, UIComponent, String): Object + getAsString(FacesContext, UIComponent, Object): String Abb. 11.2-1: Converter (UML-Notation).

Das Beispielprogramm DemoEigeneKonverter verdeutlicht den Einsatz und die Implementierung eines eigenen Konverters. Im Folgenden soll für die folgende Stadt-Klasse ein Konverter entwickelt werden.

Beispiel 1

public class Stadt { private int plz; private String name;

Stadt.java

// Getter und Setter ... @Override public String toString() { return plz + " " + name; } }

In dem Objekt der Klasse Stadt können der Name der Stadt (name) und die Postleitzahl (plz) gespeichert werden. JSF kann einen String nicht automatisch in ein Objekt vom Typ Stadt konvertieren. Stattdessen wird hierfür ein eigener Konverter entwickelt. public class StadtConverter implements Converter { public Object getAsObject(FacesContext context, UIComponent component, String value) {

StadtConverter.java

104

11 Konverter *

int split = value.indexOf(’ ’); Stadt stadt = new Stadt(); stadt.setPlz(Integer.parseInt(value.substring(0,split))); stadt.setName(value.substring(split+1,value.length())); return stadt; } public String getAsString(FacesContext context, UIComponent component, Object value) { return ((Stadt)value).getPlz() + " " + ((Stadt)value).getName(); } }

Dieser Konverter akzeptiert Eingaben in der Form »PLZ Ort« also beispielsweise 44149 Dortmund.

In Tab. 11.2-1 wird eine Auflistung der in diesem Beispiel gezeigten Methoden-Parameter dargestellt. Attribut

Bedeutung

component

Komponente, die den zu konvertierenden Wert enthält.

context

Faces Context , dient zur Kommunikation mit dem Framework.

value (String)

Zeichenkette, die zu einem Objekt konvertiert werden soll.

value (Object)

Objekt, das zu einer Zeichenkette konvertiert werden soll.

Tab. 11.2-1: Parameter der Converter-Methoden.

Das Beispiel ist in dieser Form lauffähig, jedoch muss der Konverter noch bei JSF bekannt gemacht werden, damit er auch genutzt wird. Dies ist auf zwei Arten möglich. Er kann so registriert werden, dass er immer automatisch von JSF verwendet wird, wenn ein Property einer Managed Bean oder eine Managed Bean selbst vom Typ Stadt mit einem Wert gefüllt werden soll. Alternativ kann der Konverter auch manuell an eine Komponente gehangen werden. Generelle Registrierung eines Konverters Beispiel 2 StadtConverter ForClass.java

Die erste Möglichkeit (die generelle Registrierung des Konverters) erfolgt über eine Annotation in der Konverter-Klasse und gilt in der gesamten Anwendung. Dieses Beispiel zeigt die Registrierung des StadtConverterForClass: @FacesConverter(forClass = Stadt.class) public class StadtConverterForClass implements Converter { ... }

Wird ein Konverter über den Annotationsparameter forClass registriert, nutzt JSF ihn automatisch, sobald ein vom Benutzer eingegebener Wert in ein Objekt der Zielklasse (Stadt.class) konvertiert werden soll. Diese Konvertierung ist dann nötig, wenn die Eingabe in einer Managed Bean oder einem Property einer Managed Bean von dem Typ der Zielklasse gespeichert werden soll.

105

11.2 Eigene Konverter ***

Die folgende Klasse zeigt diesen Zusammenhang beispielhaft:

Beispiel 3

@Named @SessionScoped public class Adresse { private Stadt stadt; public void setStadt(Stadt stadt) { System.out.println("Gesetzt wurde:" + stadt); this.stadt = stadt; } public Stadt getStadt() { return stadt; } }

Adresse.java

Wenn eine JSF-Seite nun auf eine Managed Bean vom Typ Adresse zugreifen und

Erklärung

eine Eingabe als stadt speichern möchte, würde JSF automatisch den für diesen Vorgang konfigurierten Konverter nutzen. Der folgende Ausschnitt zeigt den Zugriff auf das Property stadt aus einer JSF-Seite heraus (es wird vorausgesetzt, dass eine Managed Bean adresse vom Typ Adresse konfiguriert ist):

index.xhtml

Die Konvertierung erfolgt in diesem Beispiel vollkommen transparent und bedarf keines weiteren Eingriffs seitens des Entwicklers. Neben der oben beschriebenen generellen Konfiguration gibt es auch noch die Möglichkeit, einen Konverter explizit zu konfigurieren. Hierzu wird ein expliziter Name in der Annotation @FacesConverter gesetzt.

Explizite Konfiguration des Konverters

Eine explizite Konfiguration sieht wie folgt aus:

Beispiel 4

@FacesConverter("stadtConverterName") public class StadtConverterName implements Converter { ... }

StadtConverter

In dem Beispiel wird nicht generell definiert, bei welchem Zieltyp welcher Konverter

Erklärung

verwendet wird, sondern dem Konverter wird eine ID zugewiesen. Unter dieser kann er später in der JSF-Seite referenziert werden. Das explizite Hinzufügen des Stadt-Konverters an eine Komponente sähe für das oben beschriebenen Beispiel wie folgt aus:

Auch in diesem Fall wird der Konverter verwendet, jedoch nur explizit bei dieser Komponente.

Name.java

106

11 Konverter *

11.3 Fallstudie Blog -Anwendung – Konverter *** Die Fallstudie kann um einen eigenen Konverter erweitert werden. Der Konverter wird es ermöglichen, das Datum auch in der Form »heute« anzugeben. Es wird dann das aktuelle Datum verwendet. GUI

Die Blog -Einträge enthalten neben »Titel« und »Text« auch ein »Datum«. Die entsprechende Seite (addEntry.xhtml) wird daher um ein Datumsfeld ergänzt. Zusätzlich müssen Fehlermeldungen ausgegeben werden. Dies wird mittels des h:messages-Tags realisiert.

Implementierung FallstudieKonverter Nachfolgend werden die Änderungen im Vergleich zum Programm FallstudieEingabemaske dargestellt.

Es wird eine Konverterklasse DateConverter implementiert, die in der Anzeigeseite addEntry.xhtml verwendet wird. Dazu muss diese um die Annotation @FacesConverter ergänzt

werden. Die Datei addEntry.xhtml sieht folgendermaßen aus: addEntry.xhtml



...

...

Für die Ausgabe der Fehlerbeschreibungen wird das h:messages-Tag verwendet. JSF kann in diesem Beispiel keine automatische Konvertierung für das Datum vornehmen. Zwar liefert JSF einen passenden Konverter zur Konvertierung in ein Datum mit (DateTimeConverter), dieser wird jedoch (im Gegensatz zu allen anderen mitgelieferten Konvertern) von JSF nicht automatisch angezogen, da die Formatierungsangaben unter-

schiedlich sein können (z. B. 13.04.2018, 2018–04–13). Dies könnte zwar über das Locale ermittelt werden, jedoch muss festgelegt werden, ob eine Eingabe mit Datum und Uhrzeit (oder nur Datum bzw. nur Uhrzeit) vorhanden ist. Dies kann nicht automatisch festgelegt werden. DateConverter.java

@FacesConverter("dateConverter") public class DateConverter implements Converter { public Object getAsObject(FacesContext context, UIComponent component, String value) { if("heute".equalsIgnoreCase(value)) return new Date(); SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); Date date = null; try { date = sdf.parse(value); } catch(ParseException ex) { ex.printStackTrace(); throw new ConverterException ( new FacesMessage("Datum kann nicht konvertiert werden") );

11.3 Fallstudie Blog -Anwendung – Konverter ***

107

} return date; } public String getAsString(FacesContext context, UIComponent component, Object value) { Date date = (Date)value; SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); String dateText = sdf.format(date); if(dateText.equals(sdf.format(new Date()))) return "heute"; return dateText; } }

Die Konfiguration des Konverters erfolgt hier explizit. Der Konverter kann unter der Angabe des Namens dateConverter verwendet werden. Für jede Komponente wird ein neues Objekt der Klasse erzeugt. In diesem Beispiel wird ein eigener Konverter entwickelt. Der zu entwickelnde Konverter soll – unabhängig von der im Browser eingestellten Locale – ein Datum in der Form dd.MM.yyyy (z. B. 07.02.2018) und zusätzlich eine Angabe in der Form »heute« akzep-

tieren. Wenn der Benutzer »heute« in das Feld eingibt, wird automatisch das aktuelle Datum verwendet. Soll das Datum ausgegeben werden, wird für den Fall, dass das auszugebende Datum dem aktuellen Datum entspricht, ebenfalls »heute« ausgegeben. Eine mögliche Internationalisierung wird der Einfachheit halber außen vor gelassen. In dem Konverter wird die Klasse SimpleDateFormat verwendet, um das String-Objekt in ein Date-Objekt zu verwandeln und umgekehrt. Falls es sich bei dem Datum um das aktuelle Datum handelt, ist dies ein Sonderfall: Hier wird als Repräsentation »heute« verwendet statt des Datums. Im Fehlerfall wird eine ConverterException ausgelöst. JSF wird dann eine entsprechende Fehlermeldung erzeugen, die durch das h:messages-Tag angezeigt wird. Die Abb. 11.3-1 zeigt die beteiligten Managed Bean -Objekte der Fallstudie. CDI Request Scope

Session Scope

:AddEntryBean

:BlogManager

Referenz :dateConverter

:AddEntryBean

Referenz :BlogEntry

:BlogManager

Abb. 11.3-1: Beteiligte Managed Bean -Objekte der Fallstudie Konverter.

Im Vergleich zum Programm FallstudieEingabemaske ist lediglich das Objekt dateConverter hinzugekommen. Dieses wird für jede Komponente neu erstellt. Falls in einer JSF-Seite mehrere Komponenten diesen Konverter nutzen, dann wird für jede Komponente ein neues Objekt erzeugt.

Managed Bean Objekte

12

Validierung *

Eine zentrale Aufgabe einer Webanwendung ist die Validierung der vom Benutzer eingegebenen Daten. Oft müssen verschiedene Bedingungen geprüft werden: Wurde ein Wert eingegeben, ist ein Text lang genug oder eine Zahl ausreichend hoch. In den nächsten Kapiteln werden verschiedene Aspekte der Validierung thematisiert: 

»JSF-Validierung«, S. 109

 

»Darstellen von Validierungsfehlern«, S. 113 »Aussetzen der Validierung«, S. 117

 

»Eigene Validierung«, S. 120 »Bean Validation«, S. 124



»Fallstudie Blog-Anwendung – Validierung«, S. 128

12.1 JSF-Validierung * Mithilfe von Validierungs-Tags, die Eingabekomponenten-Tags hinzugefügt werden können, kann JSF Feldvalidierungen durchführen. Validierungen können natürlich manuell erfolgen. Eine solche Vorgehensweise ist mit JSF jedoch nicht nötig. JSF bringt bereits eine deklarative Validierungsfunktionalität mit, die für solche Prüfungen gedacht ist. Dabei werden die von dem Benutzer eingegebe-

JSF bringt eine Unterstützung für Validierungen mit

nen Werte bereits durch JSF geprüft und erst dann (über das Value Binding ) in die Managed Bean geschrieben, wenn sie entsprechend der festgelegten Validierungsregeln gültig sind. Fehlerhafte Validierungen führen also dazu, dass die Werte nicht in die Managed Bean geschrieben werden. Das Beispielprogramm DemoValidierung zeigt häufig verwendete Validierungen. Eine der einfachsten und zugleich häufigsten Prüfungen ist, ob überhaupt ein Wert in einem Feld eingegeben wurde. Damit JSF dies prüft, muss das required-Attribut für das entsprechende Tag gesetzt werden. Das könnte für Textfelder, die die beiden Properties nachname und vorname einer Managed Bean benutzer darstellen, so aussehen (auf die Implementation der Klasse

Beispiel 1

für die Managed Bean wird nicht eingegangen):

pflichtfelder

JSF wird in diesem Fall die beiden Eingaben des Benutzers im entsprechenden For-

Erklärung

mular erst in der Managed Bean benutzer speichern, wenn der Nachname eingegeben wurde. Wird nur der Vorname durch den Benutzer eingetragen, wird JSF weder den Vornamen noch den Nachnamen in der Managed Bean speichern. Es müssen also alle Eingaben des Benutzers valide sein, damit JSF die Werte übernimmt. Jedes JSF-Tag, das Eingaben von einem Benutzer erlaubt, unterstützt das required-Attribut (also beispielsweise auch h:inputTextarea). Zur Überprüfung dieser Funktion kann in der setVorname()-Methode der entsprechenden Managed Bean -Klasse eine Ausgabe hinzugefügt werden.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert durch Springer Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2020 M. Goll, JavaServer Faces, https://doi.org/10.1007/978-3-658-31803-1_12

Validierung.xhtml

110

12 Validierung *

Falls lediglich in dem Feld vorname etwas eingetragen und die Schaltfläche OK betätigt wird, wird der Wert nicht gesetzt (die setVorname()-Methode wird nicht ausgeführt und somit erscheint die Ausgabe nicht). Erst bei einer Eintragung in das Feld nachname werden beide Felder nach dem Betätigen der Schaltfläche gesetzt. Weitere Validierungen

Neben dieser einfachen Prüfung gibt es aber auch die Möglichkeit, umfangreichere Prüfungen durchzuführen. Hierzu werden sogenannte Validatoren verwendet. Validatoren werden als »Kind« an eine Eingabekomponente gehangen. Der Validator wird dann den Eingabewert des Benutzers prüfen und ein Speichern des Wertes in der Ma-

naged Bean nur zulassen, falls dieser der entsprechenden Validierung genügt. Beispiel 2

In diesem Beispiel wird JSF die Werte nur in der Managed Bean speichern, wenn die Eingabe mindestens »drei« Zeichen lang ist. Dazu wird das f:validateLength-Tag hinzugefügt.

laengen Validierung.xhtml

Erklärung

Ablauf bei einem Value Binding ohne Validierung



Hier wird zum h:inputText-Tag explizit das f:validateLength-Tag hinzugefügt. Der Benutzer muss jetzt mindestens drei Zeichen in das Textfeld eingeben. Wie diese Validierung innerhalb von JSF funktioniert, kann man besser nachvollziehen, wenn man die normale Vorgehensweise (ohne Validierung) kennt, die von JSF bei einem Value Binding verwendet wird. Diese wird in der Abb. 12.1-1 dargestellt.

Schritt 1

Vorname

test speichern

abbrechen

Komponente Schritt 2

inputText

Managed Bean Schritt 3

Abb. 12.1-1: Ablauf: Value Binding.



Schritt 1: Der Benutzer gibt einen Text in das Feld ein und drückt die speichernSchaltfläche.



Schritt 2: Die Eingabe des Benutzers wird von der h:inputText-Komponente auf dem Server aus dem Request ausgelesen und in dieser gespeichert.



Schritt 3: Der Wert der h:inputText-Komponente wird in dem dazugehörigen Value

Binding, also der entsprechenden Managed Bean, gespeichert.

111

12.1 JSF-Validierung *

Soll eine Validierung mittels eines Validierungs-Tags durchgeführt werden, wird zwischen Schritt 2 und Schritt 3 das Validierungs-Tag genutzt, um die Eingabe zu prüfen. Nur wenn die Validierung erfolgreich ist, wird anschließend auch Schritt 3 durchge-

Ablauf bei einem Value Binding mit Validierung

führt. Die Grafik sieht dann wie in der Abb. 12.1-2 aus.

Schritt 1

Vorname

test speichern

abbrechen

Komponente Schritt 2

inputText

Validierung

Schritt 3

Managed Bean

Schritt 4

Abb. 12.1-2: Ablauf: Value Binding mit Validierung.

Es gibt diverse Standard-Validierungs-Tags, die zum Standardumfang von JSF gehören. Es folgt eine kurze Übersicht. Das Tag f:validateLength validiert die Länge der Eingabe. Es wird die Eingabe des Benutzers nur akzeptieren, wenn sie länger ist als das Minimum (falls ein Minimum angegeben wird) und nicht größer ist als das Maximum (falls ein Maximum angegeben wird) (vgl. Tab. 12.1-1). Attribut

Bedeutung

minimum

Minimale Länge der Eingabe.

maximum

Maximale Länge der Eingabe.

Tab. 12.1-1: Attribute des Tags f:validateLength.

In diesem Beispiel muss der eingegebene Nachname mindestens drei und höchstens

Beispiel 3

acht Zeichen lang sein.



Das Tag f:validateLongRange validiert die Größe des eingegebenen, ganzzahligen Wertes. Der Zahlenwert muss größer sein als das Minimum (falls angegeben) und kleiner als das Maximum (falls angegeben) (vgl. Tab. 12.1-2).

laengen Validierung MinMax.xhtml

112

12 Validierung *

Attribut

Bedeutung

minimum

Minimale Größe des Wertes.

maximum

Maximale Größe des Wertes.

Tab. 12.1-2: Attribute des Tags f:validateLongRange.

Beispiel 4 zahlenbereich Validierung.xhtml

In diesem Beispiel muss das eingegebene Alter zwischen 18 und 120 sein.



Das Tag f:validateDoubleRange validiert ebenfalls die Größe eines eingegebenen Zahlenwertes. Diesmal handelt es sich jedoch um einen Double-Wert mit Nachkommastellen. Der Zahlenwert muss größer sein als das Minimum (falls angegeben) und kleiner als das Maximum (falls angegeben) (vgl. Tab. 12.1-3). Attribut

Bedeutung

minimum

Minimale Größe des Wertes (auch als Ganzzahl).

maximum

Maximale Größe des Wertes (auch als Ganzzahl).

Tab. 12.1-3: Attribute des Tags f:validateDoubleRange.

Beispiel 5

In diesem Beispiel wird das Gewicht als Zahl mit Nachkommastelle im Bereich 10.00 und 200.00 erwartet.

zahlenbereich Validierung Double.xhtml



Das Tag f:validateRegex validiert einen Wert mittels Regular Expressions (vgl. Tab. 12.1-4). Attribut

Bedeutung

pattern

Regular Expressions (Gesuchtes Muster in einem Wert)

Tab. 12.1-4: Attribute des Tags f:validateRegex.

Beispiel 6

Im folgenden Beispiel muss der eingegebene Text vier bis zehn Zeichen lang sein und sowohl eine Zahl als auch einen Großbuchstaben enthalten. Ein gültiger Eingabewert wäre 1abC.

regex Validierung.xhtml



12.2 Darstellen von Validierungsfehlern **

113

Diese vorgestellten Tags sind die Standard-Validierungs-Tags, die von JSF mitgeliefert werden. Es werden durch Drittanbieter weitere Validierungs-Tags angeboten (sowohl kommerzielle als auch nicht-kommerzielle), die sich in JSF integrieren lassen.

12.2 Darstellen von Validierungsfehlern ** Mit den Tags h:message und h:messages können Fehlermeldungen ausgegeben werden. Das Definieren von Standard-Validierungen geht mit JSF relativ einfach und sie funktionieren problemlos. Eine Information an den Benutzer ist jedoch immer notwendig. Zwar verhindert JSF das Schreiben des Wertes in die entsprechende Managed Bean, jedoch erfährt der Benutzer nicht, dass ein Fehler vorliegt. Wenn eine Navigation Rule definiert ist, wird diese nicht ausgeführt. Hier könnte der Benutzer vermuten, dass

Validierung ohne Fehlermeldung

etwas nicht stimmt. Wenn der Benutzer bei einer korrekten Eingabe jedoch auf derselben Seite bleiben soll und daher keine Navigation Rule definiert ist, wird er bei einer Falscheingabe unter Umständen keinen Unterschied feststellen: Die Seite wird einfach erneut geladen. In jedem Fall ist die Information an den Benutzer, dass seine Eingaben nicht valide sind, unabdingbar. Da JSF jedoch nicht erahnen kann, an welcher Stelle auf der Seite

JSF unterstützt Validierungen

Fehlermeldungen am geeignetsten dargestellt werden sollen, gibt es zwei Tags, mit denen Fehlermeldungen ausgegeben werden können. So kann der Entwickler genau festlegen, wo auf der Seite diese Meldungen erscheinen. Die beiden Tags werden im Folgenden erläutert. Mit dem h:messages-Tag lassen sich alle Fehlermeldungen ausgeben, die auf der Seite aufgetreten sind (vgl. Tab. 12.2-1). Attribut

Bedeutung

globalOnly

Gibt an, ob alle Fehlermeldungen angezeigt werden, oder nur Fehlermeldungen, die nicht von einer Komponente verursacht wurden (mehr zu diesem Typ Fehlermeldung im Kapitel »Eigene Validierung«, S. 120).

showDetail

Gibt an, ob Details zu der Fehlermeldung angezeigt werden sollen.

showSummary

Gibt an, ob eine Zusammenfassung der Fehlermeldung angezeigt wird.

Tab. 12.2-1: Attribute des h:messages-Tags.

Eine Zusammenfassung ist beispielsweise »Validierungsfehler«, die detaillierte Meldung: »[ID] Eingabe erforderlich« (als »[ID]« wird ein String angezeigt, der die Komponente eindeutig identifiziert). Weiterhin gibt es noch folgende Eigenschaften:  

errorClass/errorStyle



infoClass/infoStyle



warnClass/warnStyle

fatalClass/fatalStyle

h:messages

114

12 Validierung *

Mit diesen Attributen lassen sich die jeweiligen Classes /CSS-Styles festlegen, die bei der jeweiligen Fehlerstufe (error/fatal/info/warn) verwendet werden. Setzt man den Wert border:1px solid red; auf das Attribut errorStyle, so werden Fehler vom Typ error mit einem

roten Rahmen versehen. Die folgenden Beispiele des Programms DemoValidierungsfehler sollen den Sachverhalt verdeutlichen. Beispiel 1

Das Eingabefeld für den Nachnamen ist ein Pflichtfeld. Wird die Schaltfläche OK gedrückt, ohne dass ein Nachname eingegeben wurde, wird eine Fehlermeldung ausgegeben.

message Beispiel.xhtml

Erklärung





Die Fehlermeldung kann je nach verwendeter JSF-Implementierung etwas unterschiedlich sein. Hier wird eine Fehlermeldung ausgegeben, falls der Benutzer keine Eingabe tätigt (Abb. 12.2-1). Die Angaben j_idt6 und j_idt7 beziehen sich auf die Komponenten-IDs im Komponentenbaum, die vom JSF-Framework vergeben werden. Der Wert j_idt6 bezieht sich auf das Formular (h:form) und der Wert j_idt7 bezieht sich auf das Eingabefeld innerhalb des Fomulars (h:inputText).

Abb. 12.2-1: Darstellung: Einfache Fehlermeldung.

h:message

Mit dem h:message-Tag lassen sich Fehlermeldungen ausgeben, die bei einer bestimmten Komponente aufgetreten sind. Während bei dem h:messages-Tag alle Fehlermeldungen der Seite »an einer« Stelle ausgegeben werden, können hier die Fehlermeldungen für eine bestimmte Komponente sinnvoll auf der Seite platziert werden. Im Regelfall sollte das in der Nähe der Komponente sein, die den Fehler verursacht hat. Dieses Tag sollte vorzugsweise eingesetzt werden, da es die Nutzbarkeit einer Anwendung stark verbessert. Die Attribute sind nahezu identisch mit denen des h:messages-Tags. Es fehlt das globalOnly-Attribut, dafür gibt es ein weiteres Attribut, dass das h:messagesTag nicht hat (vgl. Tab. 12.2-2). Attribut

Bedeutung

for

Gibt an, für welche Komponente Fehler angezeigt werden sollen.

Tab. 12.2-2: Attribut des h:message-Tag. ID der Komponenten

Um die Komponente im for-Attribut angeben zu können, für die Fehlermeldungen generiert werden sollen, müssen die Komponenten mit einer ID versehen werden. Über diese ID kann die Komponente dann referenziert werden.

115

12.2 Darstellen von Validierungsfehlern **

Die Verwendung wird im nachfolgenden Beispiel gezeigt:

Beispiel 2





message

Bei jedem JSF-Tag kann also, zusätzlich zu den komponentenspezifischen Eigenschaften, auch immer eine ID vergeben werden. Hier wurde dem ersten h:inputText-

Erklärung

ForBeispiel.xhtml

Tag die ID vorname gegeben, dem zweiten h:inputText-Tag die ID nachname. Die IDs können beliebig vergeben werden. Sobald ein Tag eine eindeutige ID hat, können für ihn mit dem h:message-Tag Fehlermeldungen ausgegeben werden. Dazu wird die jeweilige ID der Eingabekomponente im for-Attribut des h:message-Tags angegeben (Abb. 12.2-2). In diesem Beispiel werden die Fehlermeldungen immer direkt über der jeweiligen Komponente dargestellt.

Abb. 12.2-2: Darstellung: Zwei Fehlermeldungen.

Die Qualität der Meldung und das Aussehen können noch verbessert werden. Beispielsweise ist die ID (z. B. j_idt5:nachname), die durch JSF dargestellt wird, für den Benutzer

Anpassen der Fehlermeldungen

eher verwirrend. Die Angabe j_idt5 bezieht sich auf das Formular, für das keine eigene ID vergeben wurde. Daher wurde eine ID vom JSF-Framework vergeben. Die Anpassung der Fehlertexte ist recht einfach, setzt jedoch voraus, dass man das Internationalisierungskonzept von JSF verstanden hat. Neben speziellen Property -Dateien mit Texten (auch Message Bundles genannt), die für eine oder mehrere JSF-Seiten gedacht sind, gibt es auch die Möglichkeit, globale Dateien zu definieren. Diese können dann zum Beispiel angepasste Fehlermeldungen enthalten. Im Programm DemoValidierungsfehlerAnpassen wird dies verdeutlicht. Um JSF so zu konfigurieren, dass eine solche Datei angezogen wird, muss die faces-

Anpassung in der

config.xml wie folgt erweitert werden:

faces-config.xml

jsf.beans.errormessages

Das Tag message-bundle enthält die Property -Datei, die die Fehlermeldungen enthält, die statt der Standardmeldungen ausgegeben werden. Ein Beispiel für eine Property -Datei, die die Meldung für leergelassene Pflichtfelder

Property -Datei mit Fehlermeldungen Beispiel 3

überschreibt: javax.faces.component.UIInput.REQUIRED= Das ist ein Pflichtfeld, bitte füllen Sie dieses Feld aus.

errormessages .properties

116

12 Validierung *

Erklärung

Anstelle der im Beispiel 2 angegebenen Fehlermeldung wird jetzt die in der Datei definierte Fehlermeldung ausgegeben. Auch hier ist es möglich, für unterschiedliche Sprachen unterschiedliche Dateien (mit entsprechendem Sprachenkürzel) zu verwenden, um die Fehlermeldungen in unterschiedlichen Sprachen zu definieren.

Platzhalter

Zur Ausgabe der für den Fehler ursächlichen Komponente in Form ihrer ID kann der Platzhalter {0} genutzt werden. Das Formular sollte ebenfalls eine ID erhalten, da sonst die automatisch vom JSF-Framework vergebene ID in der Fehlermeldung angezeigt wird.

Beispiel 4 errormessages .properties

Die Property -Datei sieht beispielsweise wie folgt aus: javax.faces.component.UIInput.REQUIRED= {0} ist ein Pflichtfeld, bitte füllen Sie dieses Feld.

Es gibt noch zahlreiche weitere Schlüssel, die mit geeigneten Fehlermeldungen überschreiben werden können. Im Folgenden wird eine kurze Auflistung gezeigt, wobei die ..._detail Meldung die Detailmeldung ist. Bei der ersten Fehlermeldung (javax.faces.component.UIInput.REQUIRED) wäre die generelle Fehlermeldung z. B. Validierungsfehler und die detaillierte Meldung (javax.faces.component.UIInput.REQUIRED_detail) [ID] Eingabe erforderlich. required



javax.faces.component.UIInput.REQUIRED (bzw. REQUIRED_detail)

 

Ein Pflichtfeld wurde nicht ausgefüllt. Platzhalter: {0} (ID)

f:validate



javax.faces.validator.LengthValidator.MAXIMUM (bzw. MAXIMUM_detail)

Length



Der Wert ist größer als das Maximum.

 

Platzhalter: {0} (Maximum), {1} (ID)

 

Der Wert ist kleiner als das Minimum. Platzhalter: {0} (Minimum), {1} (ID)

javax.faces.validator.LengthValidator.MINIMUM (bzw. MINIMUM_detail)

f:validate



javax.faces.validator.DoubleRangeValidator.MAXIMUM (bzw. MAXIMUM_detail)

DoubleRange



Der Wert ist größer als das Maximum.

 

Platzhalter: {0} (Maximum), {1} (ID)



Der Wert ist kleiner als das Minimum.

 

javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE (bzw. NOT_IN_RANGE_detail)

 

Eingegebener Wert liegt nicht im validen Bereich. Platzhalter: {0} (von), {1} (bis), {2} (ID)

javax.faces.validator.DoubleRangeValidator.MINIMUM (bzw. MINIMUM_detail)

Platzhalter: {0} (Minimum), {1} (ID)

f:validate



javax.faces.validator.LongRangeValidator.MAXIMUM (bzw. MAXIMUM_detail)

LongRange



Der Wert ist größer als das Maximum.

 

Platzhalter: {0} (Maximum), {1} (ID)

 

Der Wert ist kleiner als das Minimum. Platzhalter: {0} (Minimum), {1} (ID)

javax.faces.validator.LongRangeValidator.MINIMUM (bzw. MINIMUM_detail)

117

12.3 Aussetzen der Validierung **

 

javax.faces.validator.LongRangeValidator.NOT_IN_RANGE (bzw. NOT_IN_RANGE_detail)



Platzhalter: {0} (von), {1} (bis), {2} (ID)

Eingegebener Wert liegt nicht im validen Bereich.



javax.faces.validator.RegexValidator.MATCH_EXCEPTION (bzw. MATCH_EXCEPTION_detail)

f:validate

 

Der Aufruf der Methode getPattern() verursacht eine PatternSyntaxException. Keine Platzhalter

Regex

 

javax.faces.validator.RegexValidator.NOT_MATCHED (bzw. NOT_MATCHED_detail)

Der eingegebene Wert entspricht nicht dem festgelegten Muster.



Keine Platzhalter

 

javax.faces.validator.RegexValidator.PATTERN_NOT_SET (bzw. PATTERN_NOT_SET_detail)

Es wurde kein Muster angegeben.



Keine Platzhalter

12.3 Aussetzen der Validierung ** Manchmal kann es notwendig sein, die Validierung einer Seite bei bestimmten Aktionen zu deaktivieren. Zum Beispiel wenn die Schaltfläche »Abbrechen« gedrückt wird. Wenn die Schaltfläche »Abbrechen« gedrückt wird, sind Validierungen in der Regel unerwünscht: Der Benutzer möchte die aktuelle Aktion abbrechen, die eingegebenen Werte brauchen nicht übernommen zu werden. Daher sollte auch keine Fehlermeldung erscheinen. Für JSF handelt es sich aber auch bei der Schaltfläche »Abbrechen« um eine gewöhnliche Schaltfläche, auf die eine Aktion folgt. Daher würde auch beim Auslösen dieser Schaltfläche eine Validierung durchgeführt werden. Den Ablauf zeigt (verallgemeinert) die Abb. 12.3-1.

Schritt 1

Vorname

test speichern

abbrechen

Komponente Schritt 2

Schritt 3

inputText

Validierung Managed Bean

Schritt 4

Schritt 5

Aktionen werden ausgeführt

Abb. 12.3-1: Validierung beim Auslösen von Abbrechen.

118

12 Validierung *

 

Schritt 1: Der Benutzer gibt einen Text ein. Schritt 2: Der Text wird von der Eingabekomponente aus dem Request ausgelesen und intern gespeichert.

Aussetzen der Validierung

 

Schritt 3: Der intern gespeicherte Text wird validiert. Schritt 4: Der Text wird in die Managed Bean übernommen (falls er gültig war).



Schritt 5: Die Aktion der Schaltfläche wird durchgeführt.

Es gibt jedoch eine Möglichkeit, die Validierung auszusetzen. Dazu benutzt man das immediate-Attribut der Schaltfläche. Ist dieses Attribut gesetzt, wird die Schaltflächen-

Aktion noch vor der Validierung (also immediate – sofort) durchgeführt und die eingegebenen Werte werden weder validiert noch durch das entsprechende Value Binding in die Managed Bean übernommen. Stattdessen wird die (über das Method Bin-

ding ) definierte Aktion der Schaltfläche ausgeführt und anschließend entsprechend navigiert. Der Ablauf stellt sich (verallgemeinert) wie in Abb. 12.3-2 dar.

Schritt 1

Vorname

test speichern

abbrechen

Komponente Schritt 2

Schritt 3 Schritt 4

inputText

Immediate-Aktion wird ausgeführt Validierung Managed Bean

Schritt 5

Abb. 12.3-2: Validierung beim Auslösen mit immediate.

Schritt 4 und Schritt 5 sind in der Abb. 12.3-2 durch eine Linie getrennt dargestellt, da diese Schritte nach Durchführung der immediate-Aktion nicht mehr ausgeführt werden. Das Ausführen der Schaltflächenaktion geschieht hier bereits in Schritt 3. Die Zusammenhänge werden im Programm DemoValidierungAussetzen verdeutlicht. Beispiel 1 beispiel1.xhtml

Erklärung

Die Seite beispiel1.xhtml verdeutlicht die Verwendung des immediate-Attributes.



Hier kann der Benutzer einen Text (vorname) eingeben und anschließend auf speichern drücken. Die Aktion speichern wird jedoch nur ausgeführt, wenn auch ein Text

12.3 Aussetzen der Validierung **

119

eingegeben wurde. Dies wird durch das required-Attribut der h:inputText-Komponente erzwungen. Drückt der Benutzer jedoch abbrechen, wird sofort die abbrechenAktion durchgeführt und es findet keine Validierung statt. In diesem Fall muss der Benutzer keinen Text in dem Textfeld eingeben. Wird das Feld für den Vornamen leer gelassen und anschließend die Schaltfläche speichern betätigt, wird eine Fehlermeldung ausgegeben. Beim Drücken der Schaltfläche abbrechen wird keine Fehlermeldung ausgegeben. Anstatt beim Auslösen der speichern-

Schaltfläche dieselbe Seite neu zu laden, wäre es auch möglich, auf eine andere Seite zu verzweigen. Um das Beispiel einfach zu halten, wurde darauf verzichtet. Neben diesen eher oberflächlichen Beobachtungen passiert bei diesem Beispiel aber noch etwas Anderes: Bei abbrechen wird der eingegebene Wert, unabhängig davon,

Detailliertere Betrachtung

ob er valide ist oder nicht, nicht in dem zugehörigen Value Binding (hier #{benutzer.eingabe1}) gespeichert (wie in der Abb. 12.3-2 dargestellt). Bei speichern wird der Wert hingegen im Value Binding gespeichert (wie in der Abb. 12.3-1 dargestellt).

Neben der Möglichkeit, das immediate-Attribut auf der h:commandButton-Komponente zu setzen, gibt es auch die Möglichkeit, das immediate-Attribut auf der h:inputText-Komponente zu setzen. Das Setzen des immediate-Attributes auf der h:inputText-Komponente hat zur Folge, dass die Validierung noch vor der Validierung der anderen (nicht-immediate ) Komponenten und auch vor der Ausführung einer Aktion einer immediate -h:commandButton-Komponente durchgeführt wird. Die Abb. 12.3-3 illustriert den Ablauf bei einer Seite, auf der sowohl eine h:inputText-Komponente existiert, die immediate ist (hier der typ), als auch eine, die nicht immediate ist (wie in den vorhergehenden Beispielen vorname).

Typ Schritt 1

Vorname

person test speichern

Schritt 2

abbrechen

Komponente

Komponente

inputText

inputText

Schritt 3

Immediate-Validierung

Schritt 4

Immediate-Aktion wird ausgeführt

Schritt 5

Schritt 6

Validierung Managed Bean

Abb. 12.3-3: Validierung mit und ohne immediate.

Managed Bean

Attribut immediate für Eingabekomponenten

120

12 Validierung *

Schritt 3 und Schritt 4 werden hier nur für die Komponente typ abgearbeitet, da diese immediate ist. Für die Komponente vorname wird nur Schritt 2 abgearbeitet. Schritt 5 und 6 würden bei dem Klick auf die Schaltfläche abbrechen gar nicht abgearbeitet (daher durch eine Linie getrennt dargestellt). Beispiel 2 beispiel2.xhtml

Erklärung

Der Quelltext beispiel2.xhtml sieht wie folgt aus:





In diesem Beispiel gibt es verschiedene Möglichkeiten, wie der Benutzer sich verhalten könnte. Jede der Möglichkeiten wird im Folgenden, inklusive des Resultats, aufgelistet (vgl. Tab. 12.3-1).

Eingabe

Aktion

Beschreibung

Keine

speichern

Es erscheint eine Fehlermeldung bei typ, da dieses Feld immediate ist und zuerst validiert wird. Da bereits diese Validierung fehlschlägt, werden die Felder, die nicht immediate sind, gar nicht geprüft.

Keine

abbrechen

Es erscheint eine Fehlermeldung bei typ, da dieses Feld immediate ist und zuerst validiert wird. Das Feld vorname wird nicht geprüft, da die Validierung der immediate -Felder fehlgeschlagen ist.

typ

speichern

Es erscheint eine Fehlermeldung bei vorname.

typ

abbrechen

Es erscheint keine Fehlermeldung. Die Validierung des Feldes vorname wird nicht mehr vorgenommen, da die Schaltfläche abbrechen immediate ist.

typ und vorname

speichern

Es erscheint keine Fehlermeldung. Beide Werte werden in ihr entsprechendes Value Binding geschrieben.

typ und vorname

abbrechen

Es erscheint keine Fehlermeldung. Dadurch, dass die Schaltfläche abbrechen immediate ist, werden die Inhalte der Textfelder nicht in das entsprechende Value Binding übernommen.

Tab. 12.3-1: Eingabemöglichkeiten.

12.4 Eigene Validierung *** Es lassen sich mit JSF auch eigene imperative Validierungen implementieren, die ebenfalls durchgeführt werden, bevor Werte in das jeweilige Value Bin-

ding der Komponenten geschrieben werden. Es wird hierbei unterschieden zwischen komponentenabhängigen und -unabhängigen Validierungen. Unterschied

Bei einer komponentenabhängigen Validierung wird der eingegebene Wert einer bestimmten Komponente validiert (beispielsweise eine E-Mailadresse). Fehlermeldungen beziehen sich auf diese eine Komponente.

12.4 Eigene Validierung ***

121

Bei einer komponentenunabhängigen Validierung findet nicht nur eine syntaktische Prüfung eines einzelnen Werts statt, sondern eine semantische (beispielsweise Authentifizierung von Benutzername- und Passwortkombination).

Komponentenabhängige Validierung In diesem Fall wird eine eigene Validierungsmethode implementiert. Um eine eigene Validierung durchzuführen, wird ein spezielles Method Binding auf der Komponente gesetzt, die diese Prüfung durchführen soll. So werden Methode und Komponente miteinander verknüpft. Das Programm DemoValidierungEigeneSyntaktisch verdeutlicht den Sachverhalt. In einer JSF-Seite validiereEmail.xhtml sieht das wie folgt aus:

Beispiel 1a



validiereEmail.xhtml

Dies setzt voraus, dass in der Managed Bean benutzer auch tatsächlich eine Methode validateEmail() existiert. Es ist nicht notwendig, die Validierungsmethode (wie hier dargestellt) ebenfalls in der Managed Bean -Klasse zu implementieren, die auch den zu prüfenden Wert enthält. Es wäre ebenso gut möglich, eine weitere Klasse anzulegen, die beispielsweise nur Validierungsmethoden enthält und das Method Binding auf diese Klasse verweisen zu lassen. Hierfür muss diese Klasse die Schnittstelle javax.faces.validator.Validator implementieren. In der JSF-Seite ist das Tag f:validator mit Angabe der Validatorklasse über das Attribut validatorId notwendig. Häufig bietet es sich jedoch an, Programmteile, die für die Darstellung einer bestimmten Seite zuständig sind, in einer Managed Bean -Klasse zusammenzufassen. Eine Methode, die eine Validierung übernimmt, muss bestimmte Aufrufparameter akzeptieren und einen bestimmten Rückgabewert haben, damit JSF sie zur Validierung nutzen kann.

Method Binding zur Validierung

Konventionen für Validierungsmethoden

Ein Beispiel für so eine Methode (hier: validateEmail()) ist im Folgenden aufgelistet:

Beispiel 1b

public void validateEmail(FacesContext context, UIComponent component, Object value) { String eingabe = (String) value; if (eingabe.indexOf(’@’)==-1) { ((UIInput)component).setValid(false); FacesMessage message = new FacesMessage("Ungültige Emailadresse"); context.addMessage( component.getClientId(context), message); } }

Benutzer.java

Die vielen Aufrufparameter der Validierungsmethode wirken vielleicht verwirrend, das dahinterliegende Konzept ist jedoch recht einfach nachzuvollziehen (vgl. Tab. 12.4-1).

122

12 Validierung *

Parameter

Funktion

FacesContext context

Dabei handelt es sich um ein Objekt, das sämtliche Informationen über den aktuellen JSF-Status hält. Dieses Objekt ermöglicht die direkte Kommunikation mit dem Framework.

UIComponent component

Die Komponente, die den zu validierenden Wert enthält.

Object value

Der zu validierende Wert, der vom Benutzer eingegeben wurde. Tab. 12.4-1: Aufrufparameter der Validierungsmethode.

Diese Aufrufparameter müssen bei jeder Validierungsmethode vorhanden sein. Der Kern der Methode ist das Prüfen des eingegebenen Werts (in diesem Fall wird geprüft, ob die Eingabe ein @-Zeichen enthält). Der zu prüfenden Wert wird als Parameter value übergeben. Konverter

Dieser muss nicht notwendigerweise ein String sein, wie hier angenommen. Beim Hinzufügen eines Konverters zu einer Komponente können beliebige Objekte erwartet werden. Wird kein Konverter verwendet und das Property des Value Bindings ist ein String (wie in diesem Beispiel), kann hier auch ein String erwartet werden.

Verläuft die Prüfung erfolgreich, sind keine weiteren Schritte in der Methode notwendig. Vorgehensweise bei nicht validen Werten

Wenn der Wert nicht valide ist, müssen zwei Schritte durchgeführt werden. Der erste Schritt ist, der betroffenen Komponente selbst mitzuteilen, dass der Wert, den sie enthält, ungültig ist. Dies geschieht über die setValid()-Methode. Anschließend muss auch der Benutzer mit einer Meldung in Kenntnis gesetzt werden. Dazu wird eine Faces Message mit einer Fehlermeldung erstellt und mittels der addMessage()-Methode an das JSF-Framework kommuniziert.

Die addMessage()-Methode wird in diesem Fall mit zwei Parametern aufgerufen. Die beiden Parameter sind in der Tab. 12.4-2 erläutert. Parameter

Funktion

String

Die ID der Komponente, bei der der Fehler aufgetreten ist.

FacesMessage

Die eigentliche Fehlermeldung.

Tab. 12.4-2: Parameter der addMessage()-Methode.

Bei dem ersten Parameter handelt es sich um einen String, das heißt, die KomponentenID muss hier als String übergeben werden. Um die ID einer Komponente zu ermitteln, kann die Methode getClientId() genutzt werden. Diese liefert die ID der Komponente als String zurück.

Es ist wichtig, die Komponenten-ID beim Kommunizieren der Fehlermeldung mit addMessage() zu verwenden, damit später mit dem h:message-Tag die Fehlermeldungen

ausgegeben werden können, die zu einer bestimmten Komponente gehören (mittels des for-Attributs).

12.4 Eigene Validierung ***

123

Komponentenunabhängige Validierung Die bisher vorgestellten Mechanismen lassen eine syntaktische Einzelfeldprüfung zu. Jedoch muss häufig etwas allgemeiner validiert werden. Vorstellbar wäre, dass geprüft werden muss, ob Vor- und Nachname einer Person bereits in der Datenbank existieren. Hierbei wird nicht nur ein Feld geprüft, sondern gleich zwei. Die Prüfung ist in diesem Fall nicht syntaktischer, sondern semantischer (inhaltsbezogener) Natur. Um auch solche Fälle berücksichtigen zu können, bietet JSF die Möglichkeit, Daten

Method Binding

mehrerer Felder gleichzeitig zu prüfen, nachdem sie in die entsprechenden Managed Beans geschrieben wurden. Diese Art von Validierung verhindert also nicht die Übernahme eines Wertes in die Managed Bean (die ist zu diesem Zeitpunkt schon geschehen), sondern dient der nachträglichen Validierung und Anzeige von entsprechenden Fehlermeldungen. Üblicherweise wird das in der Methode passieren, die aufgrund der Benutzeraktion (z. B. das Drücken einer Schaltfläche) bzw. des dazugehörigen Method Bindings aufgerufen wird. Der Benutzer kann beispielsweise eine »Speichern«-Schaltfläche drücken. JSF wird

Beispiel 2

dann zunächst die vom Benutzer eingegebenen Daten (hier beispielsweise Vorname und Nachname) in der Managed Bean speichern. Anschließend wird die mit der Schaltfläche (über ein Method Binding ) verknüpfte Methode speicherBenutzer() ausgeführt. Bevor die Methode den Datensatz endgültig speichert, wird sie noch eine Prüfung durchführen. Hier ist es eine Prüfung, die sicherstellt, das Vor- und Nachname noch nicht in der Datenbank existieren (benutzerExistiert()). Das Programm DemoValidierungEigeneSemantisch verdeutlicht den Sachverhalt. @Inject private FacesContext context; public String speicherBenutzer() { if (benutzerExistiert( benutzer.getVorname(), benutzer.getNachname())) { FacesMessage message = new FacesMessage(); message.setSeverity( FacesMessage.SEVERITY_ERROR); message.setSummary( "Kombination von Vor- und Nachname " + "existiert bereits"); message.setDetail( "Eine Kombination von Vor- und Nachname " + "darf nur einmal vorkommen"); context.addMessage(null,message); return ""; } return "ok"; }

In diesem Beispiel wird die Methode benutzerExistiert() verwendet, um festzustellen, ob die angegebene Kombination von Vor- und Nachname bereits in der Datenbank existiert. Für diesen Fall wird eine Faces Message erstellt, zu der die Fehlermeldung und die Fehlerdetails gesetzt werden. Diese Faces Message wird dann an den Faces

Context übergeben (addMessage()), wobei keine ID angegeben wird (im Gegensatz

Erklärung

124

12 Validierung *

zu dem addMessage()-Aufruf bei einer Fehlermeldung, die einer Komponente zugeordnet wird). Die Fehlermeldung wird also keiner Komponente zugeordnet. Es handelt sich um eine generelle Fehlermeldung. Zum Schluss wird JSF mittels return ""; angewiesen, die aktuelle Seite neu zu laden. Wichtig bei dem return-Wert ist jedoch nur, dass für ihn keine Navigation Rule existiert. Es könnte dort ebenso gut return "xyz" stehen.

Hinweis

Mittels der Methode setSeverity() kann der Schweregrad der Nachricht angegeben werden (SEVERITY_INFO, SEVERITY_WARN, SEVERITY_ERROR oder SEVERITY_FATAL). Die eingefügten Nachrichten können je nach Einstellung angezeigt werden oder nicht. Auf diese Einstellungsmöglichkeiten wird in diesem Buch nicht näher eingegangen.

12.5 Bean Validation *** Der Einsatz von Bean Validation ist eine weitere deklarative Möglichkeit zur Validierung von Eingaben in Managed Beans. Die Validierungsangaben werden hierbei direkt in den Managed Bean -Klassen vorgenommen. Die Spezifikation der Bean Validation 2.0 wurde im JSR 380 freigegeben und ermöglicht den Einsatz von Validierungsregeln direkt in der Managed Bean -Klasse. Wie bei den Validierungs-Tags handelt es sich hierbei um eine deklarative Konfiguration. Neben der Angabe der Validierungsregeln in der Managed Bean -Klasse sind keine weiteren Tags notwendig. Analog zur Definition von Validierungsregeln mittels Validierungs-Tags werden die übergebenen Werte erst in die Managed Bean geschrieben, wenn die Validierung erfolgreich war. Verwendung

Beim Einsatz von Bean Validation werden die notwendigen Einschränkungen

( Constraints) über Annotationen an den jeweiligen Properties der Managed Bean Klasse definiert werden. Die Tab. 12.5-1 zeigt einen Ausschnitt der möglichen Annotationen. Annotation

Bedeutung

Min

Festlegung, dass ein Wert nicht kleiner als der angegebene Wert sein darf.

Max

Festlegung, dass ein Wert nicht größer als der angegebene Wert sein darf.

NotBlank

Festlegung, dass textuelle Werte nicht »leer« sein oder ausschließlich aus »Leerzeichen« bestehen dürfen.

NotEmpty

Festlegung, dass ein Wert nicht »leer« sein darf. Diese Annotation kann auf die Datentypen String, Collection, Map und Arrays angewendet werden.

NotNull

Festlegung, dass ein Wert nicht »null« sein darf.

Size

Festlegung, dass ein Wert in einem bestimmten Bereich liegen muss. Diese Annotation kann auf die Datentypen String, Collection, Map und Arrays angewendet werden. Tab. 12.5-1: Annotationen Bean Validation.

12.5 Bean Validation ***

125

Die nachfolgenden Beispiele des Programms DemoBeanValidation verdeutlichen die Verwendung von Bean Validation. Zwei Textfelder werden auf einer JSF-Seite ohne Validierungsangaben eingefügt.

Beispiel 1









benutzer.xhtml

In der zugrundeliegenden Managed Bean -Klasse Benutzer werden die Constraints mittels Annotationen festgelegt. @Named @RequestScoped public class Benutzer implements Serializable { private String nachname; private String vorname; @NotNull(message = "Nachname ist Pflichtfeld") @NotBlank(message = "Nachname darf nicht leer sein") @Pattern(regexp = "[a-z-A-Z]*", message = "Ungültige Zeichen beim Nachnamen") public String getNachname() { return nachname; } public void setNachname(String nachname) { this.nachname = nachname; } @NotNull(message = "Vorname ist Pflichtfeld") @NotBlank(message = "Vorname darf nicht leer sein") @Pattern(regexp = "[a-z-A-Z]*", message = "Ungültige Zeichen beim Vornamen") public String getVorname() { return vorname; } public void setVorname(String vorname) { this.vorname = vorname; } }

Die Browserdarstellung nach dem Absenden von leeren Eingabefeldern zeigt die Abb. 12.5-1. Beide Properties nachname und vorname dürfen nicht »null« sein (@NotNull). Die Eingaben dürfen nicht ausschließlich aus Leerzeichen bestehen (@NotBlank) und müssen das Muster »Klein- und Großbuchstaben« erfüllen (@Pattern). Die klassische JSF-Validierung zielt auf die Validierung einzelner Komponenten ab. In einigen Bereichen ist jedoch es sinnvoll, die gesamte Managed Bean zu validieren, beispielsweise eine gleichzeitige Prüfung mehrere Felder. Dieser Mechanismus wird über das Tag Das f:validateWholeBean ermöglicht. Gleichzeitige Prüfungen mehrerer Felder

Benutzer.java

126

12 Validierung *

Abb. 12.5-1: Klassenvalidierung.

lassen sich auch mit eigenen Validierungskomponenten umsetzen, jedoch werden die übergebenen Werte bei Einsatz von f:validateWholeBean erst dann in die Managed Bean geschrieben, wenn die Validierung erfolgreich war. Die Tab. 12.5-2 zeigt die Attribute dieses Tags. Attribut

Bedeutung

disabled

Aktivierung oder Deaktivierung dieser Validierungskomponente.

id

ID der zu erstellenden UIInput-Komponente.

validationGroups

Komma-separierte Liste von Validierungsgruppen. Eine Validierungsgruppe muss als vollqualifizierter Klassenname angegeben werden.

value

Angabe der zu validierenden Managed Bean über eine Value Expression. Tab. 12.5-2: Attribute von f:validateWholeBean.

Diese Funktionalität muss über einen Eintrag in der Konfigurationsdatei web.xml aktiviert werden. web.xml

javax.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN

true

Durch Verwendung des Tags f:validateBean können Validierungsangaben verfeinert werden. Beispielsweise können Gruppierungen angegeben oder die implizite Validierung deaktiviert werden. Die Tab. 12.5-3 zeigt die Attribute dieses Tags. Attribut

Bedeutung

disabled

Aktivierung oder Deaktivierung dieser Validierungskomponente.

validationGroups

Komma-separierte Liste von Validierungsgruppen. Eine Validierungsgruppe muss als vollqualifizierter Klassenname angegeben werden. Tab. 12.5-3: Attribute von f:validateBean.

12.5 Bean Validation ***

Auf einer JSF-Seite sollen ein Passwort inkl. Passwortwiederholung eingegeben wer-

127 Beispiel 2

den. Ein eingegebenes Passwort ist nur gültig, wenn beide Eingaben identisch sind. Eine Markierungsschnittstelle ValidPasswortGroup wird als Validierungsgruppe festgelegt. ValidPasswort

public interface ValidPasswortGroup { }

Group.java

Die JSF-Seite wird nachfolgend dargestellt.

passwort.xhtml











Mittels

des

Tags

f:validateBean

wird

die

Validierungsgruppe

bei beiden Eingabefeldern festgelegt. Falls eine Validierung der einzelnen Felder aktiviert werden soll, muss zusätzlich die jsf.beans.ValidPasswortGroup

Validierungsgruppe javax.validation.groups.Default hinzugefügt werden. Das Tag f:validateWholeBean legt die Klassenvalidierung fest. Die Validierungsgruppen

müssen bei den mit f:validateBean konfigurierten Eingabefeldern und bei dem Tag f:validateWholeBean identisch sein.

Die Managed Bean -Klasse muss mit einer eigens definierten Annotation annotiert werden. Hierfür wird die Annotation ValidPasswort erstellt. @Target(TYPE) @Retention(RUNTIME) @Constraint(validatedBy = Passwort.class) public @interface ValidPasswort { String message() default "Ungültige Passwörter"; Class[] groups() default {}; Class