"
 
 1 Einführung in JavaServer Faces
"
 
 2 Die Konzepte von JavaServer Faces
"
 
 3 Standard-JSF-Komponenten
"
 
 4 Advanced JSF
"
 
 5 Verwaltung von Ressourcen
"
 
 6 Die eigene JSF-Komponente
"
 
 7 Ajax und JSF
"
 
 8 JSF und HTML5
"
 
 9 JSF und CDI
"
 
 10 PrimeFaces -- JSF und mehr
"
 
 11 Faces-Flows
"
 
 12 MyGourmet Fullstack -- JSF, CDI und JPA mit CODI kombiniert
"
 
 13 JSF und Spring
"
 
 14 MyGourmet Fullstack Spring -- JSF, Spring, Orchestra und JPA kombiniert
"
 
 15 Tobago -- JSF und mehr
"
 
 16 Eine kurze Einführung in Maven
"
 
 17 Eclipse
"
 
 18 Autoren
"
 
 19 Änderungshistorie

4 Advanced JSF

Nachdem in den vorangegangenen Kapiteln die Grundlagen von JSF im Mittelpunkt standen, wollen wir uns in diesem Kapitel den etwas weiterführenden Themen zuwenden.
Nach einer kurzen Vorstellung der Project-Stage in Abschnitt [Sektion:  Project-Stage] zeigen wir Ihnen in Abschnitt [Sektion:  Advanced Facelets] erweiterte Aspekte von Facelets, die hauptsächlich die Wiederverwendung von Inhalten betreffen. Ein zentrales Thema für beinahe jedes Webprojekt ist Templating. Abschnitt [Sektion:  Templating] zeigt daher ausführlich, wie Templating mit Facelets funktioniert. Abschnitt [Sektion:  Bookmarks und GET-Anfragen in JSF] präsentiert anschließend die Unterstützung von GET-Anfragen mit View-Parametern und - neu in JSF 2.2 - View-Actions. Abschließend werfen wir in Abschnitt [Sektion:  Die JSF-Umgebung: Faces-Context und External-Context] noch einen etwas ausführlicheren Blick auf den Faces-Context und den External-Context, bevor wir das Kapitel mit einigen Details der Konfiguration von JSF in Abschnitt [Sektion:  Konfiguration von JavaServer Faces] abschließen.
Damit die Praxis nicht zu kurz kommt, werden die vorgestellten Konzepte in den Beispielen MyGourmet 10 , MyGourmet 11 und MyGourmet 12 umgesetzt.

4.1 Project-Stage

Die in JSF 2.0 eingeführte Project-Stage ist an RAILS_ENV von Ruby on Rails angelehnt und bietet eine Möglichkeit, die aktuelle Phase des Projekts für die Entwicklung bereitzustellen. Die möglichen Werte sind in der Enum javax.faces.application.ProjectStage festgelegt und lauten folgendermaßen:
Sie können die Project-Stage überall dort einsetzen, wo Sie Code abhängig von der aktuellen Projektphase ausführen wollen.
Die Projektphase kann auf folgende Arten auf einen der oben angeführten Werte gesetzt werden:
Mit dem Ausschnitt aus der web.xml in Listing Project-Stage in web.xml setzen wird die Project-Stage zum Beispiel auf den Wert Development gesetzt.
<context-param>
  <param-name>javax.faces.PROJECT_STAGE</param-name>
  <param-value>Development</param-value>
</context-param>
Zur Laufzeit wird die aktuelle Project-Stage im Application-Objekt abgelegt und kann mit der Methode getProjectStage() von dort ausgelesen werden. Listing Überprüfen der Project-Stage10mm(Variante 1) zeigt ein kleines Codebeispiel.
FacesContext fc = FacesContext.getCurrentInstance();
Application a = fc.getApplication();
if (a.getProjectStage() == ProjectStage.Development) {
  // Beliebiger Code
}
Mit der Hilfsmethode isProjectStage(ProjectStage) im FacesContext lässt sich das vorherige Codefragment noch weiter vereinfachen. In Listing Überprüfen der Project-Stage10mm(Variante 2) finden Sie ein Beispiel.
FacesContext fc = FacesContext.getCurrentInstance();
if (fc.isProjectStage(ProjectStage.Development)) {
  // Beliebiger Code
}
JSF berücksichtigt die Project-Stage bereits in der Spezifikation an einigen Stellen und das Potenzial für weitere Einsatzgebiete ist groß.
Wenn die Project-Stage auf Development gesetzt ist, wird in jede Seite eine h:messages -Komponente eingefügt, falls diese nicht vorhanden ist. Damit wird verhindert, dass Validierungsfehler in Formularen unbemerkt bleiben.
Ein weiteres Beispiel findet sich beim Ressourcenmanagement. JSF cacht Ressourcen nur dann, wenn die Project-Stage auf Production gesetzt ist. Andernfalls werden sie bei jedem Zugriff neu geladen.
In MyFaces steuert die Project-Stage zusätzlich das Überprüfen von Seitendeklarationen auf Änderungen. Ist die Project-Stage auf Production gesetzt, aktualisiert Facelets Seitendeklarationen nach dem ersten Aufruf der Seite nicht mehr. Bei allen anderen Project-Stages werden Änderungen nach zwei Sekunden wieder berücksichtigt, was die Entwicklung erheblich vereinfacht. Mehr zu diesem Thema erfahren Sie in Abschnitt [Sektion:  Die Webkonfigurationsdatei web.xml] bei der Beschreibung des Kontextparameters .

4.2 Advanced Facelets

In allen bisherigen Beispielen haben wir Facelets nur als Seitendeklarationssprache und bessere Alternative zu JSP eingesetzt. Facelets kann aber viel mehr und bietet eine breite Palette an Features, die das Leben eines JSF-Entwicklers einfacher machen. Im Laufe dieses Abschnitts werden wir einige davon vorstellen.
Facelets stellt dazu eine eigene Tag-Bibliothek mit dem Namensraum http://xmlns.jcp.org/jsf/facelets bereit. Üblicherweise wird diese Bibliothek mit dem Präfix ui in Seitendeklarationen eingebunden. Die wichtigsten Tags daraus werden wir im Rest dieses Abschnitts präsentieren.

4.2.1 Wiederverwendung von Inhalten mit Facelets

Facelets bietet Entwicklern die Möglichkeit, Ansichten modular aufzubauen und wiederkehrende Inhalte an zentraler Stelle zu definieren. Das dafür grundlegende Konzept sind die sogenannten Kompositionen, die einen Teil eines Komponentenbaums gruppieren.
Facelets kann eine Ansicht aus einer beliebigen Anzahl von Kompositionen aufbauen, die jeweils in einem eigenen XHTML-Dokument deklariert sind. Trifft Facelets beim Aufbau des Komponentenbaums auf ein ui:include -Tag , wird das Dokument mit dem im Attribut src angegebenen Dateinamen in die Ansicht mit aufgenommen. Der Pfad des Dokuments kann absolut oder relativ zur aktuellen Ansicht angegeben sein.
Sehen wir uns das im Kontext von MyGourmet an. Listing Fragment für einen Seitenkopf definiert eine Komposition für einen Seitenkopf, der unter /WEB-INF/includes/header.xhtml abgelegt ist.
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<head><title>MyGourmet header</title></head>
<body>
  <ui:composition>
    <h:panelGroup style="width: 100; height: 40px;"
        layout="block">
      <h:graphicImage value="/images/logo.png"
          style="float: left;"/>
      <h1 style="display: inline; margin-left: 5px;">
        #{msgs.title_main}
      </h1>
    </h:panelGroup>
    <h2>#{pageTitle}</h2>
  </ui:composition>
</body>
</html>
In Listing Einfügen des Seitenkopfs mit ui:include sehen Sie den Ausschnitt der Seitendeklaration showCustomer.xhtml mit dem über ui:include eingefügten Seitenkopf. Das Tag ui:param übergibt den Text für die Überschrift zweiter Ordnung als Parameter an das eingefügte Seitenfragment - doch dazu später mehr.
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <head>
    <title>#{msgs.title_main}</title>
  </head>
  <body>
    <ui:include src="/WEB-INF/includes/header.xhtml">
      <ui:param name="pageTitle"
          value="#{msgs.title_show_customer}"/>
    </ui:include>
    ...
  </body>
</html>
Wie funktioniert in Facelets die Zusammensetzung der Deklaration showCustomer.xhtml mit dem eingefügten Fragment header.xhtml ? Wie Sie vielleicht bemerkt haben, handelt es sich bei beiden Dateien um komplette XHTML-Dokumente. Es soll allerdings nur ein einziges Dokument an den Browser geschickt werden. Des Rätsels Lösung liegt darin, wie Facelets das Tag ui:composition behandelt - es ignoriert beim Einfügen des Seitenfragments sämtliche Inhalte außerhalb des Tags ui:composition .
Nachdem Facelets das HTML-Grundgerüst des Seitenfragments in Listing Fragment für einen Seitenkopf sowieso ignoriert, kann es auch entfernt werden. Das Wurzelelement des XHTML-Dokuments ist nicht länger html , sondern ui:composition . Streng genommen handelt es sich dann nicht mehr um ein XHTML-Dokument, was aber Facelets nichts ausmacht. Listing Fragment für einen Seitenkopf zeigt die optimierte Variante von header.xhtml .
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <h:panelGroup style="width: 100; height: 40px;"
      layout="block">
    <h:graphicImage value="/images/logo.png"
        style="float: left;"/>
    <h1 style="display: inline; margin-left: 5px;">
      #{msgs.title_main}
    </h1>
  </h:panelGroup>
  <h2>#{pageTitle}</h2>
</ui:composition>
Der Parameter pageTitle ermöglicht eine individuelle Definition der Überschrift bei jedem Einfügen des Fragments. Werfen Sie nochmals einen Blick auf Listing Fragment für einen Seitenkopf , dann sehen Sie die Verwendung dieses Parameters. Mit dem EL-Ausdruck #{pageTitle} wird der Wert direkt im h2 -Element ausgewertet.
Das ui:component -Tag bietet die gleiche Funktionalität wie das Tag ui:composition - im Komponentenbaum wird aber zusätzlich eine Wurzelkomponente für die Komponentengruppe eingefügt.
Die hier gezeigte Vorgehensweise ist die einfachste Form, Komponentenbäume in Facelets aus mehreren Kompositionen aufzubauen. Wir werden Ihnen im Laufe der nächsten Abschnitte noch weitere Möglichkeiten zeigen, Seitendeklarationen modular aufzubauen.

4.2.2 Tag-Bibliotheken mit Facelets erstellen

Wir haben mittlerweile mit der Core-, der HTML- und der Facelets-Tag-Library drei verschiedene Tag-Bibliotheken kennengelernt. Jede von ihnen bietet unter einem im System eindeutigen Namensraum verschiedenste Tags zum einfachen Aufbau von Seitendeklarationen an. Wie wäre es, wenn Sie für Ihre eigenen Komponenten, Konverter und Validatoren auch eigene Tags definieren könnten? Das würde die tägliche Arbeit mit JSF doch erheblich vereinfachen. Facelets bietet auch dafür eine einfache Lösung an.
Eine benutzerdefinierte Tag-Bibliothek erlaubt die Definition von Tags für eigene Komponenten, Konverter und Validatoren. Wie die Tag-Bibliotheken der Standardkomponenten hat auch jede benutzerdefinierte Tag-Bibliothek einen im System eindeutigen Namensraum, mit dem sie in jede Seitendeklaration eingebunden werden kann. Neben Tag-Definitionen kann eine Tag-Bibliothek auch sogenannte EL-Funktionen enthalten, mit denen statische Funktionen in EL-Ausdrücken verfügbar gemacht werden.
Nachdem wir bis jetzt noch keine eigenen Komponenten erstellt haben, werden wir mit der Definition eines entsprechenden Tags noch bis Kapitel Kapitel:  Die eigene JSF-Komponente warten. In Abschnitt Sektion:  Die eigene Komponentenbibliothek finden Sie sogar eine kurze Anleitung zum Aufbau einer eigenen Komponentenbibliothek. Wir wollen den folgenden Abschnitt mit der Definition einer EL-Funktion beginnen, wobei wir Ihnen auch gleich zeigen, wie Sie eine Tag-Bibliothek erstellen und im System registrieren können. Was wir Ihnen überdies nicht vorenthalten wollen, ist das Erstellen eines Tags für einen Konverter und einen Validator.

4.2.2.1 Definition einer EL-Funktion

JavaServer Pages ab Version 2.1 und Facelets bieten die Möglichkeit, statische Funktionen in EL-Ausdrücken verfügbar zu machen - und das mit einer beliebigen Anzahl von Parametern. Nachdem die Beispiele im Buch Facelets als Seitendeklarationssprache einsetzen, werden wir uns an dieser Stelle auf die Definition einer EL-Funktion mit Facelets beschränken. In JSP funktioniert die Definition allerdings sehr ähnlich.
Tipp: EL-Funktionen sind mit der neuen Version der Unified-EL in Java EE 6 oft nicht mehr notwendig, da beliebige Methoden - auch mit Parametern - aufgerufen werden können (siehe Abschnitt Sektion:  Erweiterungen der Unified-EL in Java EE 6 ).
Als Beispiel implementieren wir eine Funktion, die für ein Geburtsdatum das Alter berechnet und als Zahl zurückliefert. Der dazu notwendige Java-Code beschränkt sich auf wenige Zeilen in der statischen Methode getAge der Klasse MyGourmetUtil . Diese Klasse ist in Listing Java-Code der EL-Funktion zu sehen.
public class MyGourmetUtil {
  public static int getAge(Date birthday) {
    Calendar birthCal = Calendar.getInstance();
    birthCal.setTime(birthday);
    Calendar today = Calendar.getInstance();
    int age = today.get(Calendar.YEAR)
        - birthCal.get(Calendar.YEAR);
    if (today.get(Calendar.DAY_OF_YEAR) 
        < birthCal.get(Calendar.DAY_OF_YEAR))
      age--;
    return age;
  }
}
Die hinter der EL-Funktion liegende Methode ist damit vorhanden, jetzt muss sie noch in einer Tag-Bibliothek verfügbar gemacht werden. Listing Tag-Bibliothek mit einer EL-Funktion zeigt die Tag-Bibliothek mygourmet.taglib.xml mit der Definition der EL-Funktion in einem function -Element.
<facelet-taglib version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/
      web-facelettaglibrary_2_2.xsd">
  <namespace>http://at.irian/mygourmet</namespace>
  <function>
    <function-name>getAge</function-name>
    <function-class>
      at.irian.jsfatwork.gui.util.MyGourmetUtil
    </function-class>
    <function-signature>
      int getAge(java.util.Date)
    </function-signature>
  </function>
</facelet-taglib>
Der Name, unter dem die Funktion später in EL-Ausdrücken einsetzbar ist, wird im Kindelement function-name angegeben. Die Klasse und die aufzurufende Methode werden in den Elementen function-class und function-signature definiert. Sie müssen in beiden Werten qualifizierte Namen verwenden, damit die jeweiligen Klassen gefunden werden.
Das Einbinden der Tag-Bibliothek in die Anwendung erfolgt mit dem Kontextparameter javax.faces.FACELETS_LIBRARIES in der web.xml . Facelets interpretiert den Wert dieses Parameters als eine über Semikolons separierte Liste von Tag-Bibliotheken. Nach der Registrierung ist die Tag-Bibliothek über die im Element namespace definierte URI http://at.irian/mygourmet im System verfügbar. Listing Tag-Bibliothek in der web.xml einbinden zeigt den Ausschnitt der web.xml -Datei.
<context-param>
  <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
  <param-value>/WEB-INF/mygourmet.taglib.xml</param-value>
</context-param>
Facelets bindet Tag-Bibliotheken aus Jar-Dateien im Classpath automatisch ein, wenn sie im META-INF -Verzeichnis liegen und ihr Dateiname mit .taglib.xml endet.
Dem Einsatz der EL-Funktion steht jetzt nichts mehr im Weg. Die benutzerdefinierte Tag-Bibliothek mygourmet.taglib.xml wird ähnlich den bestehenden Tag-Bibliotheken eingebunden. In Listing EL-Funktion im Einsatz sehen wir die Ausgabe des Alters unter Zuhilfenahme unserer Funktion. Bitte beachten Sie, dass beim Aufruf der Funktion das Präfix mg: mit angegeben werden muss.
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:mg="http://at.irian/mygourmet">
  ...
  <h:outputText value=
      "#{mg:getAge(customerBean.customer.birthday)}"/>
  ...
</html>

4.2.2.2 Definition eines Konverter-Tags

In Abschnitt Sektion:  Benutzerdefinierte Konverter haben wir bereits einen Konverter für die Postleitzahl definiert und unter dem Bezeichner at.irian.ZipCode registriert. Eingebunden wurde dieser Konverter mit f:converter unter Angabe dieses Bezeichners. Schön wäre es, wenn ein eigenes Tag mit einem sprechenden Namen und der Möglichkeit, Attribute an diesen Konverter zu übergeben, existieren würde - nichts einfacher als das.
Das Hinzufügen der Zeilen in Listing Definition eines Konverter-Tags zu unserer Bibliothek reicht aus, um den Konverter unter dem Tag convertZipCode zur Verfügung zu stellen. Beim Aufbau des Komponentenbaums fügt Facelets dann für jedes Tag mit dem Namen convertZipCode aus unserer Bibliothek den Konverter mit dem Bezeichner at.irian.ZipCode ein.
<tag>
  <tag-name>convertZipCode</tag-name>
  <converter>
    <converter-id>at.irian.ZipCode</converter-id>
  </converter>
</tag>
Listing Einsatz des benutzerdefinierten Konverter-Tags zeigt das neue Tag in einer Seitendeklaration. Als Voraussetzung gilt auch hier, dass die Tag-Bibliothek in der Deklaration unter dem Präfix mg bekannt gemacht wurde.
<h:inputText id="zipCode" size="30"
    value="#{addressBean.address.zipCode}">
  <mg:convertZipCode/>
</h:inputText>
In MyGourmet 12 (siehe Abschnitt [Sektion:  MyGourmet 12: GET-Unterstützung] ) erstellen wir einen weiteren benutzerdefinierten Konverter mit eigenem Tag zum Umwandeln von Collections in Zeichenketten. Im nächsten Abschnitt über Validatoren zeigen wir Ihnen, wie auch Attribute übergeben werden können.

4.2.2.3 Definition eines Validator-Tags

Der Vorgang der Definition eines Tags für einen Konverter funktioniert in exakt der gleichen Weise auch für Validatoren. Obwohl in MyGourmet die Validierung über Bean-Validation abgewickelt wird, werden wir hier einen Validator für das Alter einer Person registrieren. Der Validator soll über die beiden optionalen Eigenschaften minAge und maxAge steuerbar sein.
Listing Definition eines Validator-Tags zeigt die Zeilen für die Definition des Validator-Tags. Der interessante Aspekt an diesem Validator sind die beiden Eigenschaften minAge und maxAge .
<tag>
  <tag-name>validateAge</tag-name>
  <validator>
    <validator-id>at.irian.Age</validator-id>
  </validator>
</tag>
Die Werte der beiden Eigenschaften können direkt über Attribute des Tags an den Validator übergeben werden. Facelets verknüpft diese dann automatisch mit gleichnamigen Eigenschaften der dahinterliegenden Validator-Objekte. In Listing Einsatz des benutzerdefinierten Validator-Tags sehen Sie das Tag mg:validateAge mit gesetztem Attribut minAge im Einsatz.
<h:inputText id="birthday" size="30"
    value="#{customerBean.customer.birthday}">
  <f:convertDateTime pattern="dd.MM.yyyy"/>
  <mg:validateAge minAge="18"/>
</h:inputText>

4.2.3 MyGourmet 10: Advanced Facelets

Das Beispiel MyGourmet 10 fasst alle Änderungen aus Abschnitt [Sektion:  Advanced Facelets] zusammen. Ein Großteil der Neuerungen hat direkt oder indirekt mit der neuen Tag-Bibliothek /WEB-INF/mygourmet.taglib.xml zu tun, die unter dem Namensraum http://at.irian/mygourmet in der Anwendung verfügbar ist.
Alle Ansichten haben jetzt einen einheitlichen Seitenkopf, der über mg:pageHeader eingebunden ist. Genauso gut wäre es möglich, direkt das dahinterliegende Seitenfragment header.xhtml aus dem Verzeichnis /WEB-INF/includes über ui:include zu verwenden.
Der Konverter für die Postleitzahl in editAddress.xhtml und der Validator für das Alter des Kunden in editCustomer.xhtml sind jetzt direkt über Tags aus der neuen Bibliothek eingebunden. In der Ansicht showCustomer.xhtml wird zusätzlich das Alter der Person über die EL-Funktion mg:getAge ausgegeben.

4.3 Templating

Layout und Design spielen bei der Entwicklung vieler Webanwendungen eine wichtige Rolle. Neben einem ausgefeilten grafischen Design ist eine konsistente und durchgängige Seitenstruktur oft die Grundvoraussetzung für den Erfolg einer Applikation. Ein einheitliches Seitenlayout vereinfacht nicht nur die Bedienbarkeit für den Benutzer, sondern ermöglicht auch die konsequente Umsetzung eines Unternehmensdesigns (Corporate Identity) auf allen Seiten. Diese Anforderungen lassen sich in der Entwicklung mithilfe von Templates umsetzen.
Der Einsatz von Templates verringert nicht nur die Redundanz der erstellten Anwendung, sondern bietet auch entscheidende Vorteile während der Entwicklung. Templates fördern durch den modularen Aufbau der Seiten die Wiederverwendung von Code und erleichtern die Trennung von Design und Inhalt. Diese Entkopplung unterstützt eine konsequente Durchsetzung des Designs im gesamten Projekt und schwächt die Auswirkung von nachträglichen Änderungen ab. Im Idealfall muss dann nur das Template oder ein zentral definiertes Seitenfragment angepasst werden, was Entwicklungs- und Wartungskosten spart.
Facelets bietet eine sehr elegante Templating-Lösung, die perfekt in den JSF-Lebenszyklus integriert ist. Ein Template ist in Facelets in erster Linie eine XHTML-Datei - wie jede andere Seitendeklaration. Den Unterschied macht das Tag ui:insert aus der Facelets-Tag-Library , mit dem ersetzbare Bereiche im Template definiert werden können. Eine Seitendeklaration, die auf diesem Template aufbaut (der sogenannte Template-Client), kann diese Bereiche mit dem eigentlichen Content ersetzen. Die komplette Ansicht besteht dann aus dem im Template definierten Inhalt und den ersetzten Bereichen aus dem Template-Client.
Sehen wir uns nun anhand eines kleinen Beispiels an, wie das Templating mit Facelets in der Praxis aussieht. Die Seiten dieses Beispiels sollen über eine Kopfzeile, einen Content-Bereich und eine Fußzeile verfügen. Diese Anforderung setzen wir in Form eines Templates mit der entsprechenden Struktur und drei ersetzbaren Bereichen um. Dadurch ist das Layout zentral definiert und einfach auf alle Seiten anwendbar. Das entsprechende Template mit dem Namen template.xhtml ist in Listing Beispiel eines Templates in Facelets zu finden.
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<head>
  <title>MyGourmet</title>
  <link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
  <div id="header">
    <ui:insert name="header">
      <h1>MyGourmet</h1>
    </ui:insert>
  </div>
  <div id="content">
    <ui:insert name="content"/>
  </div>
  <div id="footer">
    <ui:insert name="footer">
      <h:outputText value="Copyright (c) 2012"/>
    </ui:insert>
  </div>
</body>
</html>
Das Template ist ein einfaches XHTML-Dokument, in dem die grundlegende Seitenstruktur mit div -Elementen abgebildet ist. Die drei ui:insert -Tags mit den Namen header , content und footer definieren die ersetzbaren Bereiche. Bei den ui:insert -Bereichen für die Kopf- und die Fußzeile nutzen wir die Möglichkeit, Default-Content zu definieren. Falls ein Template-Client den entsprechenden Bereich nicht überschreibt, fügt Facelets den Inhalt innerhalb des ui:insert -Tags in die Ausgabe ein. Diese Vorgehensweise ist besonders dann praktikabel, wenn der Inhalt in weiten Teilen der Applikation gleich bleibt.
Im nächsten Schritt werden wir die Seite showCustomer.xhtml erstellen. Sie basiert auf unserem Template und definiert einen eigenen Content-Bereich. Facelets bietet dafür die Tags ui:composition und ui:define an. ui:composition stellt eine Verbindung zum Template mit dem im Attribut template angegebenen Namen her - in unserem Fall template.xhtml . Innerhalb von ui:composition können die Zielbereiche des Templates mit ui:define -Blöcken überschrieben werden. Welcher mit ui:insert definierte Bereich des Templates durch den ui:define -Block ersetzt wird, bestimmt das Attribut name .
Bevor wir etwas genauer analysieren, wie Facelets eine Ansicht mit einem Template rendert, werfen wir in Listing Beispiel eines Template-Clients in Facelets noch einen Blick auf den kompletten Template-Client showCustomer.xhtml . Auch hier haben wir auf das XHTML-Grundgerüst verzichtet und direkt das Tag ui:composition als Wurzelelement verwendet.
<ui:composition template="template.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <ui:define name="content">
    <h2>Kundendaten</h2>
    <h:panelGrid id="grid" columns="2">
      <h:outputText value="Vorname:"/>
      <h:outputText value="#{customer.firstName}"/>
      <h:outputText value="Nachname:"/>
      <h:outputText value="#{customer.lastName}"/>
    </h:panelGrid>
  </ui:define>
</ui:composition>
Wie baut Facelets die Ansicht aus showCustomer.xhtml mit dem Template auf? Wie schon beim Einsatz von ui:include ignoriert Facelets auch hier sämtliche Inhalte außerhalb ui:composition und der Aufbau des Komponentenbaums beginnt mit dem referenzierten Template. Während des Seitenerstellungsvorgangs werden die mit ui:insert definierten Bereiche im Template ersetzt. In unserem Beispiel kommt der Inhalt der Kopf- und Fußzeile aus dem Template und der Inhalt des Content-Bereichs stammt aus dem ui:define -Block in showCustomer.xhtml .
Abbildung Ersetzbare Bereiche des Templating-Beispiels zeigt die ersetzbaren ui:insert -Bereiche des Templates anhand der gerenderten Ausgabe unseres Beispiels. Die Umrahmungen mit den Namen der einzelnen Teile in der linken oberen Ecke dienen nur der besseren Visualisierung und wurden nicht von JSF gerendert.
Abbildung:Ersetzbare Bereiche des Templating-Beispiels
Facelets bietet für das Templating noch einiges mehr als die im letzten Abschnitt beschriebene Basisfunktionalität. Nach der Vorstellung von mehrstufigem Templating in Abschnitt [Sektion:  Mehrstufiges Templating] werden wir in Abschnitt [Sektion:  Mehrere Templates pro Seite] einen Blick auf den Einsatz von mehreren Templates in einem Template-Client werfen.

4.3.1 Mehrstufiges Templating

Mehrstufiges Templating ermöglicht den Aufbau einer Hierarchie von Templates. Das ist besonders dann praktisch, wenn eine Anwendung in mehrere Bereiche gegliedert ist, die ein gemeinsames Layout, aber unterschiedliche Inhalte haben. Im Fall von MyGourmet wäre das zum Beispiel ein Kundenbereich zum Bestellen von Gerichten, ein Bereich für Restaurants und Anbieter und ein allgemeiner Administrationsbereich. Das grundlegende Layout der Seiten mit Kopfzeile, linker Seitenleiste, Content-Bereich und Fußzeile bleibt gleich und wird daher im Haupttemplate aufgebaut. Dort landet auch ein Standardwert für den Inhalt der Kopf- und Fußzeile. In den abgeleiteten Templates wird die linke Seitenleiste überschrieben und mit bereichsspezifischem Inhalt gefüllt. Der Content-Bereich bleibt weiterhin leer und wird erst in den konkreten Seiten überschrieben. Abbildung Templating-Hierarchie von MyGourmet zeigt die mehrstufige Templating-Hierarchie in MyGourmet inklusive der bereits bekannten Seite showCustomer.xhtml aus dem Kundenbereich.
Abbildung:Templating-Hierarchie von MyGourmet
Der Aufbau einer mehrstufigen Templating-Hierarchie gestaltet sich einfach, da jeder Template-Client wiederum die Rolle eines Templates einnehmen kann - in Facelets gibt es keine strikte Trennung zwischen diesen beiden Rollen. Die oben erwähnten und in Abbildung Templating-Hierarchie von MyGourmet ersichtlichen Templates für die Anwendungsbereiche nehmen beide Rollen ein. Einerseits sind sie Template-Clients, da sie mit folgendem Code das Haupttemplate referenzieren:
<ui:composition template="template.xhtml">
Für die konkreten Seiten im Anwendungsbereich sind sie allerdings Templates, die neben den geerbten Inhalten aus dem Haupttemplate den Inhalt der linken Seitenleiste deklarieren. In der Seite wird das Template dann beispielsweise mit folgendem Code referenziert:
<ui:composition template="customerTemplate.xhtml">
Eine genauere Betrachtung des mehrstufigen Templatings in der Praxis folgt mit Beispiel MyGourmet 11 in Abschnitt [Sektion:  MyGourmet 11: Templating mit Facelets] .

4.3.2 Mehrere Templates pro Seite

In manchen Fällen macht es Sinn, neben einem Template für die Ansicht selbst zusätzliche Templates für wiederkehrende Bereiche der Seite zu verwenden. Denken Sie zum Beispiel an speziell gestaltete Bereiche in einer Seitenleiste oder an Vorlagen für unterschiedliche Typen von Seiteninhalten. Die bereits bekannte Methode mit ui:composition führt in diesem Fall nicht zum Erfolg, da der Content außerhalb des Tags abgeschnitten wird. Bei zwei geschachtelten ui:composition -Tags mit gesetztem template -Attribut gewinnt immer das innere - dieser Ansatz ist also für unsere Zwecke nicht brauchbar.
Facelets bietet aber auch dafür eine Lösung an. Mit ui:decorate existiert eine Variante von ui:composition , bei deren Verwendung der außerhalb des Tags liegende Code nicht abgeschnitten wird. Wie der Name bereits sagt, wird der Content innerhalb von ui:decorate mit dem Inhalt des referenzierten Templates dekoriert.
Sehen wir uns das anhand eines kleinen Beispiels an. Listing Template-Client mit mehreren Templates zeigt einen Ausschnitt aus dem Quelltext eines Template-Clients, der ein Template für die Seite und eines für eine Box in der Seitenleiste beinhaltet.
<ui:composition template="template.xhtml">
  ...
  <ui:define name="left_sidebar">
    <ui:decorate template="sideBox.xhtml">
    	<ui:param name="title" value="Meldungen"/>
      <h:outputText value="#{bean.msg}"/>
    </ui:decorate>
  </ui:define>
  ...
</ui:composition>
Innerhalb von ui:decorate wird dem referenzierten Template sideBox.xhtml mit dem Tag ui:param der Parameter mit dem Namen title übergeben. Der restliche Inhalt von ui:decorate bildet den Inhalt der Box.
Das Template für die Box ist ein XHTML-Dokument mit einer kleinen Besonderheit. Da es sich um ein Template für einen Teil der kompletten Seite handelt, darf das HTML-Grundgerüst nicht gerendert werden. Dazu dient das Tag ui:composition - diesmal allerdings ohne das Attribut template . Auf diese Weise eingesetzt definiert es einen Teilkomponentenbaum bestehend aus seinem Inhalt. Alle Elemente außerhalb werden abgeschnitten.
Der im Template-Client gesetzte Parameter title ist im Template als Variable verfügbar und wird über einen EL-Ausdruck referenziert. Der Inhalt der Box steht direkt im ui:decorate -Tag. Durch den Einsatz von ui:insert ohne das Attribut name fügt Facelets beim Rendern den kompletten Inhalt von ui:decorate ein. Das komplette Template sideBox.xhtml für die Box finden Sie in Listing Template sideBox.xhtml .
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <div class="side_box">
    <p class="header">#{title}</p>
    <ui:insert>Default body</ui:insert>
  </div>
</ui:composition>
Das Beispiel MyGourmet 11 in Abschnitt [Sektion:  MyGourmet 11: Templating mit Facelets] benutzt ebenfalls mehrere Templates pro Seite, um Boxen in der Seitenleiste zu formatieren.

4.3.3 MyGourmet 11: Templating mit Facelets

Nach der Vorstellung der Templating-Fähigkeiten von Facelets wird es Zeit, diese in unserem Beispiel anzuwenden. MyGourmet 11 erweitert das Vorgängerbeispiel MyGourmet 10 um ein einfaches, mithilfe von Templates umgesetztes Layout. Bevor wir allerdings auf die Details der Implementierung eingehen, wollen wir kurz präsentieren, wie die Anwendung im Browser aussieht. Abbildung MyGourmet 11: Kundenseite im Browser zeigt die gerenderte Ausgabe der Seite showCustomer.xhtml .
Abbildung:MyGourmet 11: Kundenseite im Browser
In diesem Beispiel kommt die bereits kurz vorgestellte mehrstufige Template-Hierarchie aus Abbildung Templating-Hierarchie von MyGourmet zum Einsatz. Das Haupttemplate mit dem Namen template.xhtml bietet nicht viel Neues. Es definiert das grundlegende Layout der Anwendung mit je einem div -Container für die Kopfzeile, die linke Seitenleiste, den Content-Bereich und die Fußzeile. Innerhalb dieser Container befindet sich je ein ui:insert -Tag mit einem eindeutigen Namen. Das in Abbildung MyGourmet 11: Kundenseite im Browser ersichtliche Design ist in einem verlinkten CSS-Dokument definiert.
Wenden wir uns nun dem Template für die Seiten im Kundenbereich ( customerTemplate.xhtml ) zu. Es leitet sich vom Haupttemplate ab und definiert die Standardinhalte der Kopf- und Fußzeile und der Seitenleiste für den Kundenbereich. Einen Ausschnitt zeigt Listing MyGourmet 11: Template für Kundenseiten .
<ui:composition template="template.xhtml">
  <ui:define name="header">
    <h:graphicImage value="/images/logo.png"/>
    <h1>#{msgs.title_main}</h1>
  </ui:define>
  <ui:define name="left_sidebar">
    <ui:include src="leftSideBar.xhtml"/>
  </ui:define>
  <ui:define name="footer">
    <h:outputText value="#{msgs.footer_left_text}"
        style="float: left;"/>
    <h:outputText value="#{msgs.footer_right_text}"
        style="float: right"/>
  </ui:define>
</ui:composition>
Die Definition der Kopf- und Fußzeile erfolgt direkt im Template - die Inhalte sind relativ überschaubar. Für die Seitenleiste kommt jedoch eine andere Vorgehensweise zum Einsatz: Ihr Inhalt wird im separaten XHTML-Dokument leftSideBar.xhtml definiert und mit dem Tag ui:include in das Template eingebunden.
Die Seitenleiste besteht aus einer Box mit einem kleinen Menü und einer Box mit aktuellen Meldungen. Die Umsetzung entspricht dem in Abschnitt [Sektion:  Mehrere Templates pro Seite] vorgestellten Beispiel für den Einsatz von mehreren Templates mit ui:decorate . Listing MyGourmet 11: linke Seitenleiste zeigt einen Ausschnitt der leftSideBar.xhtml -Datei.
<ui:composition>
  <ui:decorate template="/META-INF/templates/sideBox.xhtml">
    <ui:param name="title" value="#{msgs.menu_title}"/>
    <h:form id="menu">
      <h:panelGrid columns="1">
        <h:commandLink action="showCustomer">
            #{msgs.menu_show_customer}
        </h:commandLink>
      </h:panelGrid>
    </h:form>
  </ui:decorate>
  <ui:decorate template="/META-INF/templates/sideBox.xhtml">
    <ui:param name="title" value="#{msgs.news_title}"/>
    <p>MyGourmet - jetzt mit Facelets und Templating</p>
  </ui:decorate>
</ui:composition>

4.4 Bookmarks und GET-Anfragen in JSF

JSF vor Version 2.0 ist nur eingeschränkt in der Lage, bookmarkfähige Links zu erzeugen und mit GET-Anfragen umzugehen. Das liegt vor allem an der Tatsache, dass jeder Klick auf eine h:commandLink - oder h:commandButton -Komponente eine POST-Anfrage auslöst. Diesem Umstand wird ab Version 2.0 der Spezifikation mit einer erweiterten Unterstützung von GET-Anfragen Rechnung getragen. Das Basispaket, bestehend aus GET-Navigation (siehe Abschnitt [Sektion:  Navigation mit h:link und h:button] ) und View-Parametern (siehe Abschnitt [Sektion:  View-Parameter] ), ist bereits in JSF 2.0 enthalten. JSF 2.2 rundet dieses Paket mit View-Actions (siehe Abschnitt [Sektion:  View-Actions] ) noch weiter ab.

4.4.1 Navigation mit h:link und h:button

Die Basis der GET-Unterstützung bilden mit h:link und h:button zwei Komponenten, die als Link beziehungsweise Schaltfläche gerendert werden und eine GET-Anfrage absetzen. Der Clou ist, dass dabei trotzdem der Navigationsmechanismus von JSF zum Einsatz kommt. Zu diesem Zweck haben beide Komponenten das Attribut outcome , dessen Wert zum Auflösen der URL an den Navigation-Handler übergeben wird. Dieser versucht zuerst einen Navigationsfall zu finden, für den from-outcome mit dem Wert von outcome übereinstimmt. Bleibt die Suche erfolglos, wird der Wert von outcome direkt als View-ID interpretiert. Im Unterschied zur klassischen Navigation wird die View-ID bereits beim Rendern der Ansicht aufgelöst und nicht dynamisch in der Invoke-Application-Phase beim Postback. Dieses Konzept wird daher auch als präemptive Navigation bezeichnet.
Zur Demonstration der GET-Fähigkeiten erhält MyGourmet zwei neue Ansichten. Die erste neue Seite mit der View-ID providerList.xhtml zeigt eine Liste von Anbietern, die Essen ausliefern. Jeder Eintrag dieser Liste verweist mit einem h:link -Tag auf die Detailseite showProvider.xhtml . Listing h:link im Einsatz zeigt einen Ausschnitt der Seitendeklaration providerList.xhtml mit dem Link zur Detailseite.
<h:dataTable var="provider"
    value="#{providerBean.providerList}">
  <h:column>
    <f:facet name="header">
      <h:outputText value="#{msgs.provider_name}"/>
    </f:facet>
    <h:link outcome="showProvider" value="#{provider.name}">
      <f:param name="id" value="#{provider.id}"/>
    </h:link>
  </h:column>
</h:dataTable>
Der Wert des Attributs value wird dabei als Linktext gerendert. Wir nutzen an dieser Stelle die implizite Navigation und geben im Attribut outcome direkt die View-ID der Detailseite an. Der eindeutige Bezeichner des entsprechenden Anbieters wird mit dem Tag f:param als Kind der Linkkomponente bereitgestellt.
Listing Gerenderte Ausgabe von h:link zeigt, wie JSF die Links rendert. Hier sehen Sie auch, was der Begriff präemptive Navigation in der Praxis bedeutet. Bereits beim Rendern der Ansicht wird für jede h:link - oder h:button -Komponente die resultierende URL abhängig vom Attribut outcome bestimmt. Aktiviert der Benutzer einen der Links beziehungsweise eine der Schaltflächen, schickt der Browser eine simple GET-Anfrage an den Server und es gibt in diesem Fall keinen Postback. Deswegen ist es auch nicht notwendig, h:link und h:button in ein h:form -Tag einzubetten.
<a href="/showProvider.jsf?id=1">Pizzeria Venezia</a>
<a href="/showProvider.jsf?id=2">Rhodos</a>
<a href="/showProvider.jsf?id=3">Frying Dutchman</a>
Nachdem der Browser eine GET-Anfrage schickt, stimmt auch die URL in der Adressleiste mit der tatsächlich gerenderten Seite überein. Der Anwender kann daher auch ein Lesezeichen auf diese Ansicht setzen. Das hört sich trivial an, trifft aber bei der klassischen Navigation nicht immer zu, da die Befehlskomponenten h:commandLink und h:commandButton erst einen Postback auf die aktuelle Seite machen, bevor JSF die Navigation ausführt und eine neue Ansicht rendert. Daher hinkt die Adressleiste um eine Ansicht hinterher.

4.4.2 View-Parameter

Wenn der Benutzer den von h:link gerenderten Link aktiviert, muss JSF den übergebenen Parameter id verarbeiten und den richtigen Anbieter anzeigen. Dabei kommen die sogenannten View-Parameter ins Spiel, die Request-Parameter direkt ans Modell binden. Diese Parameter sind im Grunde nichts anderes als Eingabekomponenten, die über Request-Parameter befüllt werden. Wie bei allen Eingabekomponenten werden auch hier die Werte zuerst konvertiert und validiert.
View-Parameter werden in einer Seitendeklaration innerhalb des Tags f:metadata in Form von f:viewParam -Komponenten angegeben. Listing View-Parameter im Einsatz zeigt den f:metadata -Bereich der Ansicht showProvider.xhtml . Der darin eingebettete View-Parameter verbindet den Request-Parameter mit dem Namen id direkt mit der Eigenschaft providerBean.id in der Backing-Bean.
<f:metadata>
  <f:viewParam name="id" value="#{providerBean.id}"/>
</f:metadata>
Sie können überprüfen, ob sich der View-Parameter wie eine Eingabekomponente verhält (oder treffender gesagt eine ist): Rufen Sie die Seite im Browser mit einem nicht numerischen Wert für den Parameter id auf. Das Ergebnis ist eine Fehlermeldung des JSF-Number-Konverters, da die Eigenschaft providerBean.id den Typ long aufweist. Es ist auch ohne Weiteres möglich, einen Validator einzusetzen oder die Eigenschaft mit Bean-Validation-Metadaten zu versehen.
Nachdem der Bezeichner erfolgreich in die Bean übertragen wurde, müssen vor dem Rendern der Ansicht noch die Daten des Anbieters geladen werden. JSF 2.2 bietet dazu mit den neuen View-Actions die eleganteste Lösung an. Alternativ können Sie - speziell wenn Sie noch JSF 2.0 oder 2.1 einsetzen - das Laden der Daten auch über das System-Event PreRenderViewEvent realisieren. Wir zeigen Ihnen beide Varianten in Abschnitt [Sektion:  View-Actions] .
Wenn die Zielseite eines h:link - oder h:button -Elements View-Parameter enthält, können diese automatisch übernommen werden. Dazu muss lediglich das Attribut includeViewParams der Tags h:link und h:button auf den Wert true gesetzt werden. Als Beispiel fügen wir auf showProvider.xhtml folgenden Link auf die Seite editProvider.xhtml zum Bearbeiten des Anbieters hinzu:
<h:link outcome="editProvider" includeViewParams="true"
    value="#{msgs.edit_provider}"/>
Beim Rendern der HTML-Ausgabe dieses Links werden die View-Parameter der Zielseite als Parameter hinzugefügt. Vorausgesetzt, editProvider.xhtml definiert denselben View-Parameter wie showProvider.xhtml , ergibt sich folgende URL: /editProvider.xhtml?id=1 .

4.4.2.1 Positionierung von f:metadata

Das Tag f:metadata muss immer ein direktes Kind von f:view sein und darf nicht in ein Template oder ein mit ui:include eingefügtes Seitenfragment ausgelagert werden. Die Verbindung von Templating und View-Parametern lässt sich dennoch sehr einfach bewerkstelligen. Dazu muss das Template einen ersetzbaren Bereich für die View-Parameter definieren, der dann im Template-Client ersetzt wird. Listing Template mit View-Parametern zeigt ein Beispiel für ein Template mit dem ersetzbaren Bereich metadata .
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:f="http://xmlns.jcp.org/jsf/core">
<body>
  <f:view>
    <ui:insert name="metadata"/>
    <div id="content">
      <ui:insert name="content"/>
    </div>
  </f:view>
</body>
</html>
Listing Template-Client mit View-Parametern zeigt einen Template-Client, der auf dem zuvor definierten Template aufbaut und den View-Parameter-Bereich überschreibt. Wie Sie sehen, muss das komplette f:metadata -Tag im Template-Client deklariert werden.
<ui:composition template="template.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:f="http://xmlns.jcp.org/jsf/core">
  <ui:define name="metadata">
    <f:metadata>
      <f:viewParam name="id" value="#{bean.id}"/>
    </f:metadata>
  </ui:define>
  <ui:define name="content">
    Page content
  </ui:define>
</ui:composition>

4.4.2.2 Lebenszyklus mit View-Parametern

Wir klären jetzt noch die Frage, wie sich die Ausführung des Lebens-zyklus ändert, wenn View-Parameter ins Spiel kommen. Bei einer initialen Anfrage auf eine Ansicht handelt es sich ja immer um eine GET-Anfrage. Vor JSF 2.0 sprang bei einer solchen Anfrage die Ausführung des Lebenszyklus nach Phase 1 sofort zu Phase 6 und die Ansicht wurde gerendert.
Dieses Verhalten hat sich verändert: In Phase 1 wird zunächst geprüft, ob es einen Metadatenbereich und View-Parameter gibt. Wenn ja, wird eine neue Ansicht erzeugt, die nur die View-Parameter enthält. Damit wird dann der komplette Lebenszyklus durchlaufen. Gibt es in der Anfrage Parameter, werden die Daten in den folgenden Phasen wie bei einem Postback behandelt: Zuerst werden Sie den einzelnen View-Parametern zugeordnet, dann konvertiert und validiert und ins Modell zurückgeschrieben, falls keine Fehler aufgetreten sind. Abschließend wird die Ansicht wie bisher gerendert.

4.4.3 View-Actions

Bis jetzt haben wir Action-Methoden immer in Kombination mit h:commandButton oder h:commandLink verwendet. Diese klassischen Action-Methoden werden aber nur dann während des Lebenszyklus ausgeführt, wenn ein Benutzer durch einen Klick auf eine Schaltfläche oder einen Link einen Submit ausgelöst hat. View-Actions ermöglichen dahingegen ab JSF 2.2 das Ausführen von Action-Methoden beim initialen Laden einer Seite durch eine GET-Anfrage.
View-Actions waren ursprünglich bereits für JSF 2.0 geplant, da sie einen integralen Bestandteil der GET-Unterstützung bilden. Aus Zeitgründen haben sie es aber erst mit Version 2.2 in die JSF-Spezifikation geschafft.
Wir wollen nun unser weiter oben gegebenes Versprechen einlösen und das Laden der Anbieterdaten mithilfe einer View-Action zeigen. View-Actions werden in einer Seitendeklaration mit dem Tag f:viewAction angegeben, das analog zu f:viewParam innerhalb von f:metadata liegen muss. Die Action-Methode wird dabei wie etwa von h:commandButton bekannt als Method-Expression im Attribut action referenziert.
Listing View-Action im Einsatz zeigt den f:metadata -Bereich der Ansicht showProvider.xhtml mit der View-Action und dem View-Parameter aus dem letzten Abschnitt. Die in Abschnitt [Sektion:  Positionierung von f:metadata] definierten Regeln zur Positionierung von f:metadata gelten natürlich auch beim Einsatz von View-Actions.
<f:metadata>
  <f:viewParam name="id" value="#{providerBean.id}"/>
  <f:viewAction action="#{providerBean.loadProvider}"/>
</f:metadata>
In der referenzierten Action-Methode loadProvider werden die Anbieterdaten für die vom View-Parameter gesetzte ID geladen. Nachdem JSF View-Actions standardmäßig in der Invoke-Application-Phase ausführt, können wir ohne Probleme auf den Wert von id zugreifen. Dadurch ist aber auch gewährleistet, dass die Methode bei einem Konvertierungsfehler erst gar nicht ausgeführt wird (die Ausführung des Lebenszyklus springt ja vorher schon zur Render-Response-Phase). Wie bei allen Action-Methoden wird der Rückgabewert für die Navigation verwendet. Nachdem wir auf der Seite bleiben wollen, geben wir hier einfach null zurück. Listing Action-Methode für View-Action zeigt die Action-Methode.
public String loadProvider() {
  provider = findProvider(id);
  if (provider == null) {
    GuiUtil.addErrorMessage("error_non_existing_provider", id);
  }
  return null;
}
Die Navigation mit View-Actions funktioniert im Grunde genau so wie bei normalen Action-Methoden. Der einzige Unterschied ist, dass eine von View-Actions initiierte Navigation immer automatisch als Redirect ausgeführt wird. Listing Action-Methode für View-Action mit Navigation zeigt eine Variante der vorherigen Action-Methode, die bei nicht existierenden Anbieterdaten auf eine Fehlerseite mit der View-ID error.xhtml weiterleitet. Auf diese Art und Weise lässt sich zum Beispiel auch eine einfache Loginlösung mit View-Actions basteln: Solange der Benutzer nicht eingeloggt ist, löst die Action-Methode immer einen Redirect auf die Login-Seite aus.
public String loadProvider() {
  provider = findProvider(id);
  if (provider == null) {
    return "error";
  }
  return null;
}
Wie bereits erwähnt führt JSF View-Actions standardmäßig in der Phase Invoke-Application aus. Bei Bedarf können View-Actions allerdings in jeder Phase von 2 bis 5 durchgeführt werden. Sie müssen dazu lediglich im Attribut phase einen der folgenden Werte eintragen: , PROCESS_VALIDATIONS , UPDATE_MODEL_VALUES oder INVOKE_APPLICATION . Damit unsere View-Action in Phase 2 des Lebenszyklus in die Tat umgesetzt wird, müssten wir f:viewAction wie folgt ändern:
<f:viewAction action="#{providerBean.loadProvider}"
    phase="APPLY_REQUEST_VALUES"/>
Beachten Sie aber, dass in diesem Fall das Feld id noch nicht gesetzt ist - das wird erst in Phase 4 erledigt! Die View-Action wird ebenfalls bereits in Phase 2 des Lebenszyklus ausgeführt, wenn das Attribut immediate auf true gesetzt wird.
View-Actions werden standardmäßig nur bei initialen GET-Requests auf die Seite aufgerufen. Soll die View-Action zusätzlich auch bei Postbacks ausgeführt werden, muss das Attribut onPostback auf true gesetzt werden.

4.4.3.1 View-Actions im Vergleich zum System-Event PreRenderViewEvent

Mit JSF 2.0 oder 2.1 können Sie als Alternative zu View-Actions das System-Event PreRenderViewEvent verwenden. Mit JSF 2.2 funktioniert das natürlich weiterhin - View-Actions sind aber deutlich flexibler. Dazu registrieren wir über das Tag f:event direkt in der Seitendeklaration die Methode preRenderView der Managed-Bean als Listener für dieses Ereignis. Hier die Registrierung in showProvider.xhtml :
<f:event type="preRenderView"
    listener="#{providerBean.preRenderView}"/>
Listing Listener-Methode für PreRenderViewEvent zeigt die zuvor registrierte Listener-Methode in der Klasse ProviderBean . Hier erfolgt das Laden der Anbieterdaten, falls beim Konvertieren und Validieren des View-Parameters kein Fehler aufgetreten ist. Seit JSF 2.0 lässt sich diese Abfrage einfach über die Methode isValidationFailed am Faces-Context bewerkstelligen. Sollte kein Anbieter mit dem angegebenen Bezeichner existieren, erzeugen wir eine entsprechende Nachricht.
public void preRenderView(ComponentSystemEvent ev) {
  FacesContext ctx = FacesContext.getCurrentInstance();
  if (!ctx.isValidationFailed()) {
    this.provider = findProvider(id);
    if (provider == null) {
      GuiUtil.addErrorMessage("error_non_existing_provider", id);
    }
  }
}
Anhand des Beispiels zeigt sich auch schon der erste Unterschied zwischen View-Actions und einem Listener für das PreRenderViewEvent . Die Listener-Methode wird immer zu Beginn der Phase 6 des Lebenszyklus ausgeführt - und das sowohl für initiale Anfragen als auch für Postbacks. Das würde für unser Beispiel bedeuten, dass die Anbieterdaten bei jedem Request neu geladen werden. Nachdem die Managed-Bean aber im View-Scope liegt, ist das eigentlich nicht notwendig. Mit der View-Action werden die Daten nur beim ersten Zugriff auf die Seite über eine GET-Anfrage geladen. Und das auch nur dann, wenn die Konvertierung des View-Parameters nicht fehlschlägt.
Mit View-Actions können Sie außerdem bei Bedarf ohne Probleme in die Navigation eingreifen.
Ein weiterer Unterschied fällt nicht sofort auf den ersten Blick auf. Im Gegensatz zum Aufruf des Listeners für das PreRenderViewEvent ist zum Zeitpunkt des Aufrufs der View-Action der Komponentenbaum noch nicht aufgebaut. Dadurch ist es zwar einerseits nicht möglich auf einzelne Komponenten zuzugreifen, andererseits ergibt sich dadurch aber ein Performance-Vorteil.

4.4.4 MyGourmet 12: GET-Unterstützung

Das Beispiel MyGourmet 12 fasst alle Änderungen rund um das Thema View-Parameter und View-Actions zusammen. Die augenscheinlichste Neuerung ist der neue Anbieterbereich in der Anwendung. Als Einstiegspunkt dient die Seite providerList.xhtml mit einer Übersicht der Anbieter. Diese Liste wird beim Erzeugen der Backing-Bean ProviderBean mit einigen Werten initialisiert. Von dieser Seite führt pro Anbieter eine h:link -Komponente zu showProvider.xhtml . Die Daten eines Anbieters werden in Instanzen der Klasse Provider abgelegt.
Beim Aufruf der Seite showProvider.xhtml wird die als Request-Parameter mitgeschickte ID des Anbieters mit einem View-Parameter in der Managed-Bean gespeichert. Eine View-Action sorgt dann dafür, dass die Anbieterdaten für diese ID geladen werden.
Da wir seit MyGourmet 11 Templating benutzen, mussten wir das Template (wie in Abschnitt [Sektion:  Positionierung von f:metadata] beschrieben) für den Einsatz von View-Parametern anpassen.
Eine weitere kleine Änderung betrifft die linke Seitenleiste. Damit der Anbieterbereich erreichbar ist, haben wir das Menü um einen Link erweitert. Zur besseren Unterstützung von Bookmarking haben wir das Menü auf h:link -Komponenten umgestellt. Als angenehmer Nebeneffekt kann dadurch die Form eingespart werden.
Listing MyGourmet 12: Einsatz des Listenkonverters zeigt den Einsatz eines neuen Konverters, der Collections in Zeichenketten umwandelt. Mit dem Attribut bundleName kann ihm der Name eines Resource-Bundles übergeben werden, aus dem die Einträge der Liste aufgelöst werden. Nachdem mittlerweile der Anbieter und der Kunde eine Liste von Kategorien aufweisen, die aber nur symbolische Konstanten enthalten, spart dieser Konverter einiges an Quelltext.
<h:outputText value="#{providerBean.provider.categories}">
  <mg:convertList separator=", " bundleName="msgs"/>
</h:outputText>
Listing MyGourmet 12: Listenkonverter zeigt die getAsString -Methode und die Eigenschaften der Konverterklasse ListConverter , die über die Attribute des Custom-Tags mg:convertList gesetzt werden. Dieses Tag wurde, wie schon im letzten Beispiel gezeigt, in der Tag-Bibliothek mygourmet.taglib.xml definiert.
private String separator;
private String bundleName;

public String getAsString(FacesContext ctx,
    UIComponent comp, Object value) {
  StringBuilder builder = new StringBuilder();
  if (value instanceof Collection) {
    for (Object obj : (Collection)value) {
      String item = obj.toString();
      if (builder.length() > 0 && separator != null) {
        builder.append(separator);
      }
      if (bundleName != null && bundleName.length() > 0) {
        builder.append(GuiUtil.getResourceText(
            ctx, bundleName, item));
      } else {
        builder.append(item);
      }
    }
  }
  return builder.toString();
}

4.5 Die JSF-Umgebung: Faces-Context und External-Context

Bisher sind wir immer wieder auf den Faces-Context gestoßen. Dieser Kontext stellt die zentrale Schaltstelle einer JSF-Anwendung dar und wird durch die Klasse javax.faces.context.FacesContext repräsentiert. Er wird ganz am Anfang jeder HTTP-Anfrage vom Faces-Servlet initialisiert und steht dem Entwickler ab dann als Parameter vieler Methoden, aber auch jederzeit über den Aufruf der Methode FacesContext.getCurrentInstance() zur Verfügung.
Mit dem Faces-Context ist ein direkter Zugriff auf den EL-Resolver möglich. Damit lassen sich Objekte, die über die Unified-EL verfügbar sind, direkt im Java-Code auflösen. Listing Zugriff auf eine Managed-Bean im Java-Code zeigt zum Beispiel, wie der Zugriff auf eine Managed-Bean namens personList aussieht.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getELResolver().getValue(
    fc.getELContext(), null, "personList");
Eine wichtige Anwendung des Faces-Contexts ist das Hinzufügen von Nachrichten für die Darstellung auf der Webseite - und die Möglichkeit, auf die bisher hinzugefügten Nachrichten zuzugreifen. Zu diesem Zweck existieren folgende Methoden:
Weiterführende Details zum Hinzufügen und Verwalten von Nachrichten finden Sie in Abschnitt Sektion:  Nachrichten .
Applikationsobjekte anlegen: Über den Faces-Context gelangen Sie auch zur Application , und die hilft Ihnen sowohl beim Erzeugen von neuen Komponenten als auch von Method- und Value-Expressions (siehe Listing Applikationsobjekte anlegen ).
fc.getApplication().getExpressionFactory().
    createValueExpression(ELContext ctx,
        String expression, Class expectedType);

fc.getApplication().getExpressionFactory().
    createMethodExpression(ELContext ctx, String expression,
    Class expectedReturnType, Class[] params);

fc.getApplication().createComponent(String componentType);
Wobei der ELContext - wie in Listing Zugriff auf eine Managed-Bean im Java-Code bereits gezeigt - als Eigenschaft des Faces-Contexts verfügbar ist. Auch hier zahlt es sich aus, Utility-Methoden für das Erzeugen neuer Elemente vorzusehen.
Lebenslauf beeinflussen: Ein wichtiger Bereich im Faces-Context ist die Möglichkeit, den Lebenslauf einer HTTP-Anfrage zu beeinflussen. Dazu gibt es folgende Methoden, die bereits bei der Ereignisbehandlung in Abschnitt Sektion:  Ereignisse und Ereignisbehandlung zum Einsatz gekommen sind:
External-Context: Schließlich bietet der Faces-Context noch die Möglichkeit, auf den External-Context zuzugreifen. Der External-Context ist der Wrapper rund um die der Webanwendungsumgebung zugrundeliegende Funktionalität; also in den meisten Fällen um entweder den ServletContext oder PortletContext . Hier der für einen Zugriff notwendige Code:
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
Auch der External-Context bietet einige interessante Methoden, die sich aus der Funktionalität des Basis-Contexts ergeben. Hier eine selektive Auswahl:
Der External-Context bietet noch einige weitere Methoden, die Sie am besten der API-Dokumentation entnehmen.

4.6 Konfiguration von JavaServer Faces

JSF verwendet ein Minimum an zu editierenden XML-Dateien. Sieht man von einer eventuellen Verwendung ergänzender Bibliotheken oder Portlets ab, existieren nur zwei Konfigurationsdateien für eine JSF-basierte Webanwendung:
Abschnitt [subsec Konfiguration der Unified-EL] zeigt die Konfiguration zur Verwendung der alternativen EL-Implementierung von JBoss . Damit lassen sich auch auf älteren Servern (vor Java EE 6 ) die Features der neuen Unified-EL nutzen.

4.6.1 Die Webkonfigurationsdatei web.xml

Sehen wir uns nun im Folgenden eine typische web.xml -Datei einer JSF-Applikation genauer an. Der wichtigste Teil des Deployment-Deskriptors ist die Spezifikation des Faces-Servlets , das die Anfragen an die JSF-Anwendung bearbeitet, und dessen Mapping . Ein weiterer wichtiger Aspekt der Webapplikation, der in diesem Abschnitt behandelt wird, sind Konfigurationsparameter.

4.6.1.1 Faces-Servlet und Mapping

Jede JSF-Applikation muss ein Faces-Servlet konfigurieren. Listing Deployment-Deskriptor von MyGourmet 1 zeigt nochmals die web.xml -Datei des Beispiels MyGourmet 1 , in der das von JSF mitgelieferte Servlet javax.faces.webapp.FacesServlet inklusive des zugehörigen Servlet-Mappings definiert sind.
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
  <description>JSF 2.0 - MyGourmet 1</description>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>
      javax.faces.webapp.FacesServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>
Welche Anfrage auf welches Servlet weitergeleitet wird, zeigt das servlet-mapping -Element an. Der Wert des url-pattern -Elements definiert ein Präfix oder Postfix der Anfrageadresse, das dem Faces-Servlet zugewiesen wird. Im Beispiel MyGourmet 1 wurde die Postfix-Zuordnung (auch Extension-Mapping genannt) *.jsf gewählt, wobei sämtliche Anfragen mit der Endung .jsf durch das Faces-Servlet gehandhabt werden. Intern wird diese Adresse dann auf die View-ID der Seitendefinition umgelegt.
Die zweite Möglichkeit, ein Servlet-Mapping zu definieren, ist eine Präfix-Zuordnung <url-pattern>/faces/*</url-pattern> . Die View-ID der Seitendefinition ergibt sich in diesem Fall direkt aus der URL, nachdem das Präfix entfernt wurde.
Sehen wir uns kurz den entscheidenden Unterschied zwischen Präfix- und Postfix-Mapping anhand eines Beispiels an. Wir wollen dazu die Seite mit der Seitendefinition /helloWorld.jsp der fiktiven Webanwendung http://www.mustermann.org im Browser laden. Mit einem Postfix-Mapping müssen wir dazu die URL
http://www.mustermann.org/helloWorld.jsf
in die Adressleiste eingeben. Mit einem Präfix-Mapping sieht die URL folgendermaßen aus:
http://www.mustermann.org/faces/helloWorld.jsp
In der Regel sind beide Methoden für eine JSF-Anwendung einsetzbar. In manchen Fällen, wie beim Einsatz von Tomahawk oder Trinidad, kann es allerdings von Vorteil sein, ein Präfix-Mapping zu verwenden.

4.6.1.2 Kontextparameter

An erster Stelle in der Konfiguration stehen üblicherweise die Kontextparameter. Die optionale Angabe von context-param -Elementen dient der Definition von Parametern zur Initialisierung des Servlet-Contexts . Hier können Basiseinstellungen vorgenommen werden. Die folgende Liste zeigt eine Übersicht der wichtigsten Einstellungen für JSF:

4.6.2 Die JSF-Konfigurationsdatei -- faces-config.xml

In diesem Abschnitt geht es darum, zentrale Einstellungen für JavaServer Faces zu treffen. Einerseits bietet die faces-config.xml die Möglichkeit, alltägliche Konfigurationseinstellungen in der Entwicklung einer JSF-Applikation zu setzen. Dazu gehört die Konfiguration von Managed-Beans, Navigationsregeln sowie Einstellungen zur Applikation selbst. Der zweite Aufgabenbereich, die Registrierung von Komponenten, Renderern, Validatoren und Konvertern, ist für den Entwickler von Komponenten (und Komponentenbibliotheken) interessant. Zuletzt gibt es noch erweiterte Möglichkeiten, wie das Einbinden von Phase-Listenern und die Konfiguration von Factories für die Erzeugung von JSF-Kernklassen.
JSF bietet ab Version 2.0 für viele Einstellungen in der Konfigurationsdatei faces-config.xml alternativ die Möglichkeit, Annotationen einzusetzen.
Die Konfiguration von Managed-Beans erfolgt in jeweils einem managed-bean -Element pro Bean oder ab JSF 2.0 mit der Annotation . Eine genauere Beschreibung der umfassenden Einstellmöglichkeiten findet sich in Abschnitt subsec Konfiguration von Managed-Beans .
Die Regeln für die Navigation in einer JSF-Anwendung werden mithilfe von navigation-rule -Elementen definiert. Wie das genau funktioniert, zeigt Abschnitt Sektion:  Navigation . Im Laufe der Entwicklung einer JSF-Webapplikation können sehr viele Navigationsregeln anfallen. Um die Übersicht zu behalten, empfiehlt sich die Auslagerung einzelner Teile der Konfiguration in separate Dateien, wie es im Abschnitt der JSF-Konfigurationsdatei faces-config.xml beschrieben wurde.

4.6.2.1 Anwendungseinstellungen -- application

Ein zentrales Element in der faces-config.xml ist das Tag application . Darin werden wichtige Einstellungen für Kernbereiche von JSF getroffen. Das Element application kann als die zentrale Schaltstelle einer JSF-Anwendung betrachtet werden. Hier ist es möglich, neben Einstellungen zur Lokalisierung und der Definition von Message- und Resource-Bundles essenzielle Teile wie den View-Handler oder den EL-Resolver von JSF mit eigenen Implementierungen zu dekorieren. Damit wird dem Benutzer ein mächtiges Werkzeug in die Hand gegeben, um die Applikation an eigene Bedürfnisse anzupassen.
Listing faces-config.xml mit Spring-EL-Resolver zeigt ein Beispiel für eine faces-config.xml , auf deren Elemente wir weiter unten eingehen werden.
<faces-config>
<application>
  <navigation-handler>
    at.irian.jsfatwork.MyNavigationHandler
  </navigation-handler>
  <el-resolver>
    org.springframework.web.jsf.el.SpringBeanFacesELResolver
  </el-resolver>
  <message-bundle>
    at.irian.jsfatwork.messages
  </message-bundle>
  <locale-config>
    <default-locale>de</default-locale>
    <supported-locale>en</supported-locale>
  </locale-config>
  <resource-bundle>
    <base-name>at.irian.jsfatwork.text</base-name>
    <var>text</var>
  </resource-bundle>
</application>
</faces-config>
In der folgenden Auflistung finden Sie eine Übersicht der wichtigsten Einstellungen. Bei den einzelnen Punkten handelt es sich jeweils um Kindelemente des Tags application :
Die weiteren möglichen Elemente der faces-config.xml , wie die Registrierung der Renderer, Konverter oder Validatoren, werden in den Abschnitten Sektion:  Konvertierung und Sektion:  Validierung beziehungsweise in Kapitel Kapitel:  Die eigene JSF-Komponente (für die Konfiguration von Komponenten) noch genauer behandelt.

4.6.3 Konfiguration der Unified-EL

Falls Sie einen älteren Server (wie Tomcat 6 oder Jetty 7) einsetzen, müssen Sie trotzdem nicht auf die wichtigsten Features der neuen Unified-EL aus Java EE 6 verzichten.
Die alternative EL-Implementierung von JBoss bietet die wichtigsten Features der neuen Unified-EL , implementiert aber noch die alte API. Dadurch lassen sich Probleme mit älteren Servern vermeiden. Die JBoss-EL in Version 2.0.1.GA erhalten wir über eine Abhängigkeit in der Maven-Projektdatei pom.xml (siehe Listing Maven-Abhängigkeit zur JBoss-EL ).
<dependency>
  <groupId>org.jboss.el</groupId>
  <artifactId>jboss-el</artifactId>
  <version>2.0.1.GA</version>
  <scope>compile</scope>
</dependency>
Im zweiten Schritt müssen wir JSF davon überzeugen, die alternative Implementierung zu verwenden. Dazu muss über einen Kontextparameter die Klasse org.jboss.el.ExpressionFactoryImpl als Expression-Factory gesetzt werden. Tabelle tab:expression-factory zeigt die Namen der Kontextparameter für Apache MyFaces und Mojarra .
ImplementierungParameter
Apache MyFaces org.apache.myfaces.EXPRESSION_FACTORY
Mojarra com.sun.faces.expressionFactory