Der Sprung von JSF 2.1 auf JSF 2.2 ist zunächst ein Minor Update. Dennoch sind in diesem vermeintlich kleinen Versionssprung sehr viele Neuerungen enthalten, die beinahe schon ein Major Update gerechtfertigt hätten. Interessant an dieser neuen Version ist, dass die Java-EE-Welt näher zusammenrückt und nicht mehr jeder Standard ein separates Dasein führt. So integriert sich JSF zusehends mit CDI und lässt eigene Ansätze im @ManagedBean-Bereich auslaufen.
Im ersten Teil dieses Tutorials haben wir einen T-Shirt-Webshop aufgebaut, der bereits viele neue Funktionen von JSF 2.2 einsetzte. Wir haben das Konzept von Resource Library Contracts kennen gelernt und verschiedene Templates als Module austauschbar in der Anwendung konfiguriert. Mittels ViewActions konnten wir speziell bei Get-Requests eine Aktion vor dem Darstellen der Seite aufrufen und ggf. sogar auf andere Seiten verweisen. Natürlich basiert unser Shop auf HTML5 und damit auch auf neuen Tags und Attributen, die wir mittels PassThroughAttributes und PassThroughElements auch problemlos verwenden konnten. Über den in JSF 2.2 standardisierten FileUpload waren wir in der Lage, Dateien mit JSF-Tags an den Server zu übertragen.
Im Webshop, den wir in Teil 1 begonnen haben, haben wir bereits etliche neue Funktionen aufgenommen. Dennoch gibt es ein paar Ecken, an denen wir noch feilen können und mittels weiterer neuer Funktion von JSF 2.2 Verbesserungen nutzen können. Ein großer Punkt wird das Thema Faces Flows sein. Mittels Faces Flows können (View-)Abläufe zusammengefasst werden und über fest definierte Ablauffolgen ausgeführt werden. Zudem können mit diesem Konzept wiederverwendbare Seitenfolgen als Module ausgelagert bzw. auch einer Anwendung als JAR-Bibliothek hinzugefügt werden.
Eine weitere wichtige Eigenschaft einer Webanwendung ist es häufig, Multi-Tab- bzw. Multi-Window-fähig zu sein. Das bedeutet, dass wir unsere Anwendung in mehreren Tabs oder Fenstern quasi parallel betreiben können. Hierzu konnte man in der Vergangenheit mittels CDI Conversations schon einiges erreichen, das JSF-Framework selbst hatte dazu jedoch nichts vorzuweisen. Neu hinzugekommen ist hier das ClientWindow, das es ermöglicht, neue Tab-/Fensterinstanzen über eine dynamisch erzeugte ID zu identifizieren.
Da unser Webshop auch sicher gegenüber Hackerangriffen sein soll, ist das Thema Security natürlich von großer Bedeutung. Ein in der Praxis häufig anzutreffender Angriffsvektor ist CSRF (Cross Site Request Forgery). Für eine ausführliche Definition verweise ich einfach auf den entsprechenden Wikipedia-Artikel [1]. Kurz zusammengefasst bedeutet CSRF, dass es nicht möglich sein soll, einen ungewollten Request eines Angreifers innerhalb der eigenen legitimierten Session ausführen lassen zu können.
Beginnen wir zunächst mit Faces Flows. Faces Flows sind ein neues Konzept, um zusammenhängende Seitenflüsse inklusive der Navigationslogik modular zu verpacken. Diese Flows können damit wiederverwendbar gestaltet werden. Auch eine Bereitstellung von Flows über ein JAR-File ist angedacht und möglich. Die Idee dieser Flows ist nicht neu. Vielmehr haben Open-Source-Projekte wie Spring Web Flow, ADF Task Flows oder MyFaces CODI hier geholfen, die besten Ideen daraus in den JSF-Standard zu packen – getreu dem Motto, das Ed Burns einmal prägte: „Standards are for standardizing. Not for innovation.“
In Abbildung 1 ist die übliche Navigationslogik in JSF visualisiert. Es ist in der Regel möglich, von jeder Seite beliebig auf eine andere Seite zu springen. Zwar bietet die JSF-Navigationslogik seit 1.0 bereits die Möglichkeit, dies ein wenig einzuschränken; in der Praxis wird dies nach meiner Beobachtung jedoch nicht allzu häufig verwendet. Man arbeitet eher mit globalen Navigationsregeln, die genau dieses „wilde“ Navigieren zulassen. Mit Faces Flows wird hier ein stringenterer Mechanismus bereitgestellt, der einen klaren Pageflow bestimmt, mit einem Anfangspunkt und Ausstiegspunkten (Abb. 2).
Zusätzlich wurde ein eigener Scope dafür bereitgestellt, der @FlowScoped, der Beans genauso lange am Leben erhält, wie der Flow aktiv ist. Verlässt der Benutzer den Flow, wird die Bean zerstört.
In Listing 1 sehen Sie ein umfangreicheres Beispiel für den Bestellprozess in unserer T-Shirt-Anwendung. Es werden die entsprechenden Views für die Bestellung als View Nodes definiert. Doch diese View Nodes sind nur ein kleiner Anteil dessen, was JSF 2.2 an dieser Stelle zu bieten hat. Konkret unterstützt die Spezifikation folgende Node Types:
View Node: das sind die bereits erwähnten Views, sprich: die konkreten Seiten, die innerhalb des Flows dargestellt werden.
Method Call: kann in einem Schritt während der Flow-Abarbeitung aufgerufen werden. Die Methode ist i. d. R. eine Method Expression, die bei Auftreten evaluiert wird. Die Rückgabe der Methode kann auf weitere Nodes verweisen, z. B. eine konkrete View Node für die Anzeige aktivieren.
Switch: entspricht dem klassischen Verständnis einer Switch-Anweisung aus Java. Es wird eine Expression ausgewertet und je nach Rückgabe der Auswertung ein weiterer Node mit angezogen.
Flow Call und Flow Return: Von einem Flow aus kann ein weiterer (Sub-)Flow aufgerufen und natürlich aus diesem Flow wieder zum rufenden Flow zurückgekehrt werden.
Zusätzlich zu den erwähnten Node Types unterstützt die Spezifikation auch so genannte Initializer und Finalizer. Dies sind Einsprungpunkte (meist Expressions), die bei Beginn respektive bei Verlassen eines Flows ausgeführt werden.
Listing 1: Bestellprozess mit FacesFlows
<flow-definition id="orderflow">
<initializer>#{shopCtrl.initializeFlow}</initializer>
<start-node>product</start-node>
<view id="product">
<vdl-document>/flows/orderflow/buy-product.xhtml</vdl-document>
</view>
<view id="name">
<vdl-document>/flows/orderflow/buy-name.xhtml</vdl-document>
</view>
<view id="details">
<vdl-document>/flows/orderflow/buy-details.xhtml</vdl-document>
</view>
<view id="confirm">
<vdl-document>/flows/orderflow/buy-confirm.xhtml</vdl-document>
</view>
<flow-return id="/index">
<from-outcome>/index.xhtml</from-outcome>
</flow-return>
<flow-return id="/pages/produkte">
<from-outcome>/pages/produkte.xhtml</from-outcome>
</flow-return>
<flow-return id="/pages/impressum">
<from-outcome>/pages/impressum.xhtml</from-outcome>
</flow-return>
<finalizer>#{shopCtrl.cleanUpFlow}</finalizer>
</flow-definition>
Eine Flow-Definition wird mit einer eindeutigen ID beschrieben, über die sie auch im Programm aufgerufen werden kann. Anschließend werden die verschiedenen Navigationsmöglichkeiten definiert. Erwähnenswert ist, dass ich die drei Hauptnavigationspunkte explizit als Flow-Return aufgenommen habe. Dies bewirkt, dass zu jedem Zeitpunkt auf die Hauptmenüeinträge geklickt werden kann und damit der Flow beendet wird.
Passend zum neu gestalteten Flow wurde die dahinter liegende Bean, die Klasse ShirtOrder, von SessionScope auf FlowScope geändert. Das ist wesentlich ressourcensparender und auch vom Design her klarer als alles immer in die Session zu packen.
Listing 2: FlowScoped Bean
@Named
@FlowScoped("orderflow")
public class ShirtOrder implements Serializable {
@PostConstruct
public void afterCreation() {
System.out.println("## wurde erzeugt");
}
@PreDestroy
public void beforeDelete() {
System.out.println("## werde gelöscht");
}
...
}
Im FlowScoped Bean, das in Listing 2 abgebildet ist, wurde zunächst nur der Scope per Annotation ausgetauscht. Für erste Lernschritte habe ich zwei Methoden mit @PostConstruct und mit @PreDestroy annotiert, um genau verfolgen zu können, dass die Bean auch korrekt beim Betreten des Flows angelegt und beim Verlassen des Scopes zerstört wird.
Doch wo werden diese Flow-Definitionen abgespeichert? Die Spezifikation kennt hier wieder zwei Ablageorte: Zum einen kann im /flows-Verzeichnis in der Web-App ein Flow mitsamt allen Views gespeichert werden; zum anderen können Flows auch in JAR-Files ausgelagert werden. Dort werden Flows im META-INF/flows-Verzeichnis gesucht. Es ist somit möglich, modulare Seitenabfolgen als separate Deployment Unit zur Verfügung zu stellen und zur Deployment-Zeit in einer Anwendung bereitzustellen.
Eine Sache, die wir als Power-User im Internet immer wieder gerne nutzen, ist die Möglichkeit, in mehreren parallelen Fenstern oder Tabs zu arbeiten. Speziell bei Übersichtsseiten oder Suchergebnisseiten öffnen wir die in einer Liste dargestellten Informationen für die Detailansicht gerne in einem neuen Tab. Was aus Sicht des Users eine sehr praktische Lösung ist, stellt für den Anwendungsentwickler regelmäßig eine große Herausforderung dar. Das Problem ist, dass die genau gleiche Anwendung (inklusive der Anwendungssitzung) in mehreren Zuständen sein kann (verschiedene Seiten in verschiedenen Tabs geöffnet). Erfolgen jetzt Requests an den Server, muss dieser unterscheiden können, auf welches Fenster sich die Aktion bezieht. Da das HTTP-Protokoll diese Information nicht enthält, müssen Lösungen gebastelt werden, die eine Unterscheidung des geöffneten Fensters oder Tabs ermöglichen. Die Lösung in JSF 2.2 lautet ClientWindow. Ein ClientWindow stellt serverseitig ein Fenster bzw. Tab dar, also eine Art eigenständige Arbeitsumgebung. Damit JSF hier serverseitig eine Unterscheidung machen kann, muss im ClientRequest eine Information mitgegeben werden. Bei jedem Request muss somit eine Identifikation enthalten sein, die es dem Server (bzw. JSF) ermöglicht, das konkrete Fenster bzw. Tab zu identifizieren. Da es hierfür keinen Königsweg gibt, wie man einem Request diese Informationen mitgeben kann, kennt die Spezifikation verschiedene Varianten. Über den Kontext-Parameter javax.faces.CLIENT_WINDOW_MODE kann die gewünschte Variante gesteuert werden. Die Spezifikation kennt die Werte none und url. Eigene Erweiterungen können hier eingehängt werden. Der Wert none bewirkt, dass das Feature deaktiviert wird und kein ClientWindow zur Verfügung steht. url bewirkt, dass über URL-Parameter und ggf. Hidden Fields eine WindowId mitgegeben wird. In Listing 3 ist der Eintrag der web.xml zu sehen. Fehlt dieser Parameter, wird url als Standard gesetzt.
Listing 3: Kontextparameter für den ClientWindow Mode
<context-param>
<param-name>javax.faces.CLIENT_WINDOW_MODE</param-name>
<param-value>url</param-value>
</context-param>
Wie wirkt sich nun das Vorhandensein eines ClientWindow in JSF aus? Zunächst bewirkt es nichts Offensichtliches. Es gibt in der Spezifikation keinen Scope, der speziell an ein ClientWindow gekoppelt ist. Natürlich steht es den verschiedenen zusätzlichen Frameworks im JSF-Umfeld offen, sich genau an dieser Stelle einzuklinken und passende Scopes zu liefern. Für unser Shop-Beispiel habe ich einen sehr rudimentären Mechanismus geschrieben, in dem ich Daten pro ClientWindow ablege und somit einen eigenen Scope erzeugt habe (wie gesagt, nur sehr rudimentär zu Demonstrationszwecken). Damit ermögliche ich es, dass man sich die verschiedenen Shirts in separaten Tabs anschauen kann. Die verschiedenen Tabs werden im URL mit einem Parameter aufgerufen, der serverseitig zu verschiedenen ClientWindows führt. Die anzuzeigenden Werte mache ich von diesem Wert (bzw. von der WindowId) abhängig. Konkret habe ich eine Erweiterung auf der Produktseite eingebaut, mit der man auf eine Produktdetailseite navigiert. In dieser Detailseite wird eine Bean mitsamt ihren Properties angesprochen. Nun kann der Benutzer von der Produktseite mehrere Detailseiten in mehreren parallelen Tabs öffnen. In jedem Tab bzw. Fenster soll die passende Bean in einem „Tab-Scope“ gespeichert werden. Der Einstieg in die Detailseiten erfolgt über die Produktliste, in der eine weitere Spalte für Details abgebildet ist. Das relevante Codeschnipsel sieht folgendermaßen aus:
<h:link disableClientWindow="true" value="Details" outcome="show-details.xhtml">
<f:param name="prodName" value="#{current.name}" />
</h:link>
Ein <h:link>-Tag wird verwendet, um einen GET-Aufruf zu erzeugen, den man üblicherweise mit Open in new window bedienen kann. Da ich mich jedoch bereits in einer ClientWindow-Umgebung befinde, muss im neuen Tab sichergestellt sein, dass eine neue WindowId erzeugt und nicht die bestehende weitergereicht wird. Dies erfolgt über das Attribut disableClientWindow. Jetzt wird ein URL erzeugt, der als Parameter den Namen des Produkts übergeben bekommt (das ist jetzt nicht sonderlich elegant, hier sollte in der Praxis eine – verschlüsselte – ID verwendet werden). Auf der Zielseite muss dieser Parameter ausgewertet und die Produktdetails für diesen Scope (das Window bzw. Tab) müssen geladen werden. Hierzu verwende ich die ebenfalls in JSF 2.2 neu hinzugekommenen Viewactions. Auf diese sind wir schon im ersten Teil des Tutorials eingegangen.
Die Methode loadShirtDetails (Quellcode unten) wertet den über ViewParameter übergebenen Wert des URL aus, lädt das entsprechende Produkt und stellt es in den eigens geschaffenen Tab-Scope:
public void loadShirtDetails() {
if ( urlProdName!=null && !"".equals( urlProdName ) ) {
Shirt shirt = totalContainer.getShirtByName( urlProdName );
detailContainer.setShirtInScope( shirt );
}
}
Die Realisierung an dieser Stelle ist sehr einfach:
FacesContext jsfCtx = FacesContext.getCurrentInstance();
String wId = jsfCtx.getExternalContext().getClientWindow().getId();
storedShirtsInScope.put( wId, shirt );
Über den Aufruf getClientWindow().getId() wird der Identifier für das aktuelle Window gezogen. Dieser Wert wird verwendet, um das gerade geladene Shirt in einer Map zu speichern. Die Map liegt im SessionScope. Somit ist der neu geschaffene Tab-Scope direkt abhängig von der HttpSession. Eine elegante Lösung, wie die Daten des Tab-Scopes wieder entfernt werden, fehlt hier noch. Aber die Lösung zeigt sehr deutlich, wie mit wenig Aufwand ein clientseitiges Window oder Tab erkannt und serverseitig bearbeitet werden kann. Um die Lösung vollends abzurunden, zeige ich nun auf der Detailseite auf eine Bean, die die Daten für den aktuellen Tab bereitstellt:
<h:outputText id="col" value="#{shirtDetailContainer.currentShirt.name}" />
Die Implementierung der Methode getCurrentShirt ist in Listing 4 zu sehen.
Listing 4: Abfrage des eigenen Scopes
public Shirt getCurrentShirt() {
FacesContext jsfCtx = FacesContext.getCurrentInstance();
String wId = jsfCtx.getExternalContext().getClientWindow().getId();
if ( storedShirtsInScope.containsKey( wId ) ) {
return storedShirtsInScope.get( wId );
}
return null;
}
Über die WindowId wird in der Map das dazugehörige Shirt geladen und zurückgeliefert. Die Auswertung erfolgt bei jedem Aufruf der getter-Methode, was generell in JSF nicht zu empfehlen ist. In diesem speziellen Fall sollte dies aber keine Performanceprobleme nach sich ziehen. Wir können jetzt Daten in verschiedenen Tabs parallel offen halten und bearbeiten – und das lediglich mithilfe der Möglichkeiten der Spezifikation. Wenn hier dann JSF-Komponentenbibliotheken weitere Scopes bauen, wird es sicherlich noch um einiges komfortabler.
Ein bisschen Ajax und PPR (Partial Page Rendering) dürfen natürlich auch in diesem Webshop nicht fehlen. Um die neue Reset-Funktionalität zu demonstrieren, habe ich ein Formular mit einer kleinen Ajax-Erweiterung versehen. Bei Eingabe des Namens erfolgt per Ajax bei einem Treffer vorab die Befüllung der weiteren Felder wie Postleitzahl und Ort. Zugegeben, über die fachliche Korrektheit lässt sich streiten, sie zeigt jedoch ein neues Verhalten sehr schön:
Nachname:
<h:inputText value="#{customer.lastname}" id="last"
valueChangeListener="#{shopCtrl.lastNameChanged}">
<f:ajax render="myForm" execute="first last" />
</h:inputText>
<h:message for="last" />
In diesem Quellcodeausschnitt ist zu sehen, dass ein ValueChangelistener an dem Eingabefeld hängt, der nach Verlassen des Feldes aktiviert wird und ggf. weitere Werte in den Managed Bean Customer einstellt. Durch das <f:ajax>-Tag wird der Listener gleich nach Verlassen des Eingabefeldes angestoßen.
Da auf dem Eingabefeld für den Vornamen ein Required-Validator liegt, kann es jedoch zu Validierungsfehlern kommen (Abb. 3). Das alles funktioniert an sich recht gut. Nehmen wir jedoch einmal an, dass wir einen Reset-Button einbauen wollen, der die Maske in den Initialzustand zurücksetzt (alle Eingaben auf leer), hatten wir in JSF bislang ein Problem bei der Verwendung von Ajax. Zwar kann auf einem Reset-Button eine Aktionsmethode aufgerufen werden (die auch aktiviert wird, da execute nur auf @this beschränkt ist), aber das anschließende Re-Rendering zeigt evtl. immer noch einen Wert im Feld Nachname an:
public void clearInputs() {
customer.setFirstname( "" );
customer.setLastname( "" );
customer.setZip( "" );
customer.setCity( "" );
}
Grund ist, dass die Komponente in JSF neben der Datenhaltung in der Managed Bean einen so genannten Local Value hat. Das ist der Wert, der in der Komponente zwischengespeichert wird, solange die UpdateModel-Phase noch nicht durchlaufen wurde. Dieses Verhalten ist aus Sicht des JSF-Lifecycles korrekt und in den allermeisten Fällen auch passend zur Fachlichkeit. Im konkreten Fall eines Resets stört dieses Verhalten jedoch. Zwar wurde die Managed Bean zurückgesetzt (Quellcode oben), aber da das Eingabefeld noch einen lokalen Wert hat, wird dieser zunächst visualisiert. Um dieses Verhalten zu umgehen, muss der lokale Wert in der Komponente zurückgesetzt werden. Erst danach greift der Renderer bei einem Render-Vorgang auf den Wert in der Bean zurück. Die Lösung mit JSF 2.2 lautet resetValues. Dies ist ein Attribut, das in einem <f:ajax>-Tag gesetzt werden kann. Es weist die Komponenten an, den lokal gespeicherten Wert zurückzusetzen – genau das Verhalten, das wir hierfür benötigen. Damit ist das leidige Reset-Problem, das in der Community schon länger für Unmut gesorgt hatte, endlich beseitigt.
Das Thema Security gehört seit einigen Jahren zu einem Standard-Feature bei Webanwendungen. So ziemlich jeder Webentwickler wird die OWASP (Open Web Application Security Project, [2]) Top 10 kennen (oder zumindest schon davon gehört haben). Auch viele Angriffsvektoren sind von der Begrifflichkeit vertraut. CSRF (Cross Site Request Forgery) ist ein solches Beispiel. Jeder kennt den Begriff, aber mancher wird sich sicher schon gedacht haben: Hoffentlich fragt mich niemand nach Details. Doch genau diese Herangehensweise ist falsch. Wir als Entwickler sollten uns sehr wohl bewusst machen, wie eine Webanwendung gehackt werden kann, damit wir im Vorfeld bereits Gegenmaßnahmen ergreifen können. Speziell bei CSRF geht es darum, dass eine bösartige Anfrage (Request) einem ahnungslosen Opfer untergeschoben wird. Oder mit anderen Worten: Es wird innerhalb der Session eines Opfers ein HttpRequest abgesetzt (also im Namen des Opfers), ohne dass dieser die Aktion tatsächlich wollte. Das können einfach Anfragen nach Detailseiten sein, aber auch gravierendere Aktionen wie die Durchführung einer Überweisung. Wenn es einem Angreifer z. B. gelänge, dass folgender GET-Url ausgeführt wird, wird das Ausmaß von CSRF sehr deutlich: http://www.bankDesOpfers.com?transferMoney=1000&from=dasOpfer&To=Hacker.
Dies ist natürlich stark vereinfacht dargestellt, zeigt aber, dass wir hier Vorsicht bei unserer Webanwendung walten lassen sollten. Seit JSF 2.0 ist die GET-Unterstützung stark verbessert worden. Es können ViewParameter ausgewertet werden, und seit JSF 2.2 existieren ViewActions. PreRenderView-Events und PhaseListener können zudem auf URL-Parameter reagieren und ggf. Aktionen antriggern. Zur Vermeidung, dass ungewollte Requests abgesetzt werden und auf Serverseite verarbeitet werden, werden häufig so genannte Page Tokens eingesetzt. Ein Page Token ist eine willkürlich erzeugte Zufallszahl, die als Hidden Field oder URL-Parameter in den Requests eingebunden wird. Diese Zahl wird serverseitig ebenfalls gespeichert. Erfolgt nun ein Request vom Client an den Server, wird geprüft, ob in den Request-Parametern dieser Token vorhanden ist und dem aktuellen Wert im Server entspricht. Wenn ja, ist der Request gültig und wird verarbeitet. Es wird dann ein neuer Token generiert und an den Client zurückgesendet. JSF war hier in der Vergangenheit bereits recht gut aufgestellt. Über den ViewState musste (zumindest bei Post) ein String zur Identifizierung des ServerStates mitgesendet werden. Allerdings war dies kryptografisch nicht sehr sicher ausgearbeitet, konnte man doch mit ein wenig Aufwand diese ID berechnen. Zudem war bei GET-Requests dieser Schutz überhaupt nicht gegeben.
Mit JSF 2.2 halten so genannte Protected Views Einzug in die Spezifikation. Damit können Seiten als schützenswert deklariert werden, sodass diese nicht mehr direkt (z. B. per direkter URL-Eingabe) aufgerufen werden können. Das Aufrufen dieser geschützten Seiten ist nur noch mit gültigem Token möglich. Dieses Token wird durch JSF automatisch gesetzt, wenn über JSF-Aktionen auf diese Seite navigiert wird. CSRF wird somit praktisch unmöglich.
Im Folgenden ist ein Beispiel zu sehen, in dem lediglich die Detailseite eines Produktes geschützt wird:
<protected-views>
<url-pattern>/pages/show-details.xhtml</url-pattern>
</protected-views>
Unsere übliche Anwendungslogik funktioniert weiter tadellos. Wir können über die Produktlistenseite auf die Detailseite navigieren. Wenn wir jedoch direkt den URL aufrufen (und alle eventuell vorhandenen URL-Parameter abtrennen), wird eine ProtectedViewException erzeugt. Diese könnte ggf. mittels entsprechender Fehlerseiten behandelt werden. Es zeigt jedoch vor allem, dass ohne gültigen Token die Seite nicht aufgerufen werden kann. Der Token wird bei POST-Requests als Parameter mitgesendet. Bei GET-Requests kann man diesen Parameter sehr deutlich im URL erkennen. In unserem Shop ist der URL, den man für die Navigation auf die Detailseite verwendet z. B. http://localhost:8080/ShirtApp/faces/pages/show-details.xhtml?javax.faces.Token=1366139142819. Der eigentliche Token-Wert ist nicht deterministisch. Somit kann ein CSRF-Angriff diesen nicht vorausberechnen und unberechtigterweise die Detailseite aufrufen (und ggf. Aktionen darin vornehmen).
In diesem zweiten Teil des JSF 2.2 Tutorials haben wir weitere sehr hilfreiche und nützliche neue Features kennen gelernt. Faces Flows sind ein mächtiges Instrument. Sehr gut lassen sich diese mit Resource Library Contracts kombinieren, und man erhält eine sehr modulare und flexible Webanwendung. Das Thema Security wurde endlich auch in der Spec ein wenig mehr gewürdigt, und der CSRF-Schutz bringt einiges an Mehrwert. Es gibt jedoch noch zahlreiche weitere Features, auf die wir im Artikel nicht eingegangen sind, u. a. Ajax Delay, Composites und Non-Composites in einer Taglib, CDI-basierter ViewScope etc. Dies bleibt Ihrer Forscherfreude vorbehalten.
Alle Feature-Requests konnten natürlich auch mit dieser neuen Version von JSF 2.2 nicht berücksichtigt werden. Aber die Planungen für JSF 2.3 sind bereits im Gange. Es ist somit gut zu wissen, dass JSF nicht stehenbleibt und kontinuierlich weiterentwickelt wird.
Andy Bosch ist Trainer und Berater im Umfeld von JSF und Portlets. Auf seiner Onlinetrainingsakademie www.jsf-academy.com stellt er regelmäßig Trainingsvideos zu JSF, CDI und Portlets bereit. Er ist Autor mehrerer Bücher zu JSF und hält regelmäßig Vorträge auf nationalen und internationalen Konferenzen.
[1] http://en.wikipedia.org/wiki/Cross-site_request_forgery
[2] https://www.owasp.org/index.php/Main_Page
[3] https://maven.java.net/content/groups/public/org/glassfish/javax.faces/2.2.0-SNAPSHOT/
[4] http://weblogs.java.net/blog/edburns/archive/2011/09/26/try-out-mojarra-220-snapshot
[5] http://www.jsf-academy.com/ShirtApp.zip, Demoanwendung zum Download