@ManagedBean (name="mitarbeiterBean") @SessionScoped public class CustomerBean { private String vorname; private String nachname; private String email; //TODO Getter und Setter } @ManagedBean @RequestScope public class EmailControler { @ManagedProperty(value="#{mitarbeiterBean}") private MitarbeiterBean maBean; public String berechneEmail() { StringBuffer buffer = new StringBuffer(); buffer.append(maBean.getVorname().substring(0,1)); buffer.append(maBean.getNachname().substring(0,2)); buffer.append("@ordix.de"); maBean.setEmail(buffer.toString()); return "ausgabe"; } }
... <h:body> <h:form> <h:panelGrid columns="2"> Vorname: <h:inputText value="#{mitarbeiterBean.vorname}"/> Nachname: <h:inputText value="#{mitarbeiterBean.nachname}"/> </h:panelGrid> <h:messages/> <h:commandButton action="#{emailControler.berechneEmail}" value "Berechne E-Mail" /> </h:form> </h:body> ...
... <h:body> <h:form> Ordix E-Mail Adresse: <h:outputText value="#{mitarbeiterBean.email}"/> <h:commandButton action="eingabe" value "Zurück" /> </h:form> </h:body> ...
... <h:body> <h:form> <h:panelGrid columns="2"> Vorname: <h:inputText id=“vor“ value="#{mitarbeiterBean.vorname}"/> Nachname: <h:inputText id="nach" value="#{mitarbeiterBean.nachname}"> <f:ajax execute="@this vor" render="voll"/> </h:inputText> </h:panelGrid> <h:outputText id="voll" value="#{mitarbeiterBean.vorname} #{mitarbeiterBean.nachname}"/> <h:outputText id="email" value="#{mitarbeiterBean.email}"/> <h:messages/> <h:commandButton action="#{emailControler.berechneEmail}" value "Berechne E-Mail"> <f:ajax execute="@this vor nach" render="email"/> </h:commandButton> </h:form> </h:body> ...
@ManagedBean (name="mitarbeiterBean") @SessionScoped public class CustomerBean { @NotNull(message="Bitte geben sie einen Vornamen ein") private String vorname; @NotNull(message="Bitte geben sie einen Nachnamen ein") @Size(min=2 message="Der Nachname muss mindestens 2 Zeichen haben") private String nachname; private String email; //TODO Getter und Setter }
... <h:body> <composite:interface> <composite:attribute name="vName" required="true"/> <composite:attribute name="nName" required="true"/> </composite:interface> <composite:implementation> <h:panelGrid columns="2"> Vorname: <h:inputText value="#{cc.attrs.vName}"/> Nachname: <h:inputText value="#{cc.attrs.nName}"/> </h:panelGrid> </composite:implementation> </h:body> ...
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:oka="http://java.sun.com/jsf/composite/meineKomponenten"> ... <h:body> <h:form> <oka:vollerName vName="#{mitarbeiterBean.vorname}" nName="#{mitarbeiterBean.nachname}"/> <h:messages/> <h:commandButton action="#{emailControler.berechneEmail}" value "Berechne E-Mail" /> </h:form> </h:body> ... </html>
<f:view contentType="text/html"> <f:metadata> <f:viewParam name="nachname" value="#{mitarbeiterBean.nachname}" /> </f:metadata> <h:body> <h:form> <h:panelGrid columns="2"> Vorname: <h:inputText value="#{mitarbeiterBean.vorname}"/> Nachname: <h:inputText value="#{mitarbeiterBean.nachname}"/> </h:panelGrid> <h:messages/> <h:commandButton action="#{emailControler.berechneEmail}" value "Berechne E-Mail" /> </h:form> </h:body> </f:view>
Es liegen gut drei Jahre zwischen der Veröffentlichung von JSF 1.2 und der aktuellen Version 2.0. In der Zeit hat sich JSF stark gemausert und ist zu einem sehr guten und weit verbreiteten Framework gereift. Zahlreiche Ergänzungen wie RichFaces, IceFaces und JBoss Seam sind in dieser Zeit entwickelt worden und haben die Web-Entwicklung stark vereinfacht. JSF ist somit zu einer der wichtigsten Technologien in der Java-Web-Entwicklung geworden.
Die vielen zum Teil konkurrierenden Ergänzungen führten aber immer häufiger zu Problemen, da sie teilweise nicht kompatibel zueinander waren (und sind). Durch die Version 2.0 wurde vieles vereinheitlicht und auch bisher nicht standardisierte Funktionen wurden in den JSF-Standard mit aufgenommen.
Die auch bisher schon häufig verwendete View-Technologie Facelets wurde nun Teil des Standards. Aber die Entwicklung ging noch weiter. Das bisher bevorzugte JSP wurde vom Thron gestürzt. Facelets ist nun die bevorzugte View-Technologie und JSP in diesem Zusammenhang sogar „deprecated“. Facelets bietet die Möglichkeit, Seiten und Inhalte auf Templates basierend aufzubauen.
Seitenfragmente lassen sich einfach zentral ablegen und in mehrere Seiten integrieren. Das geht sogar so weit, dass man Komponenten erstellen kann, ohne eine Zeile Java-Code zu verfassen.
Auf diesen Aspekt wird im Verlauf des Artikels näher eingegangen, wenn es um die Composite Components geht.
Für diejenigen, die Konfigurationsdateien noch nie leiden konnten, hier nun eine gute Nachricht: Mit JSF 2.0 ist es möglich, in den meisten Fällen auf die faces-config.xml zu verzichten. Dies wird einerseits durch den verstärkten Einsatz von Annotations erreicht, aber auch durch das Prinzip „Convention over Configuration“. Dies bedeutet, dass es ein Default-Verhalten gibt, wenn keine explizite Konfiguration angegeben wurde. Wer weiterhin die faces-config.xml benutzen möchte, kann dies natürlich tun.
Wie dies funktioniert, schauen wir uns am folgenden Beispiel an. In Abbildung 1 sehen wir zwei ManagedBeans. Die eine Bean enthält die Daten (Model), die andere die Action-Methode(n) (Controller). In der alten JSF-Version mussten beide Beans in der faces-config.xml eingetragen werden, damit sie in den JSF-Seiten benutzt werden konnten. Dies erfolgt nun über die Annotation @ManagedBean. Wenn man keinen Namen angibt, über den man die Bean ansprechen kann, wird als Name der Klassenname verwendet, bei dem der erste Buchstabe zu einem Kleinbuchstaben umgewandelt wird. In diesem Fall gilt das für die Klasse EmailController, die man in der xhtml-Seite unter dem Namen emailController ansprechen kann (siehe Abbildung 2). Der ManagedBean CustomerBean haben wir expliziert den Namen mitarbeiterBean gegeben, unter dem man die Bean ansprechen kann (siehe Abbildung 2).
Mit den Annotations kann man ebenfalls den Scope angeben, in dem eine Bean läuft. In unserem Fall läuft die CustomerBean im Session Scope und die Bean EmailController im Request Scope (siehe Abbildung 1). Werden keine Annotations angegeben, so unterstellt JSF 2.0 den Reqest Scope. Neu hinzugekommen ist in JSF 2.0 der View Scope (@ViewScoped). Dieser bezieht sich genau auf eine View. Beans, die in diesem Scope abgelegt werden, sind so lange gültig, wie eine View aktiv ist. Dies kann nur einen Request lang dauern oder beliebig viele. Erst wenn auf eine andere View navigiert wird, wird dieser Scope geleert.
Die Annotation @ManagedProperty ist ebenfalls neu. In unserem Beispiel ist die ControllerBean (EmailController) stateless und liegt im Request-Scope. Beim Instanziieren wird die CustomerBean (die im Session-Scope läuft) mit Hilfe der neuen Annotation @ManagedBean injiziert, so dass auf sie zugegriffen werden kann (siehe Abbildung 1).
Auch die Navigation ist in vielen Fällen nun ohne Konfigurationsdatei möglich. Der Rückgabewert der Action-Methode kann nun gleich das Navigationsziel sein. In unserem Beispiel ist der Rückgabewert der Methode berechneEmail() der String-Ausgabe. Somit wird nach einer Datei ausgabe.xhtml gesucht und dorthin navigiert (siehe Abbildung 1).
Eine weitere Möglichkeit besteht darin, direkt im Action-Attribut des CommandButtons oder der CommandLinks einen View-Namen anzugeben, zu dem navigiert werden soll. In unserem Beispiel ist dies in der Abbildung 3 zu sehen. Dort steht beim CommandButton action="eingabe", womit beim Drücken des Buttons zu eingabe.xhtml navigiert wird.
Bisher war AJAX in JSF-Programmen nur durch Erweiterungen verfügbar. Allerdings waren die verschiedenen Ansätze nicht kompatibel zueinander.
Aus diesem Grund standarisiert JSF 2.0 den AJAX-Support. Um die neue AJAX-Integration in JSF vorzustellen, ändern wir unser Beispiel so ab, dass die Ausgabe der E-Mail auf der gleichen Seite erfolgt und beim Drücken des Buttons nur das Ergebnisfeld aktualisiert und nicht die ganze Seite neu geladen wird.
Ferner bauen wir noch ein Ausgabefeld ein, in dem der gesamte Name (Vorname + Nachname) angezeigt wird. Dieses Feld wird beim Verlassen des Eingabefeldes für Nachname aktualisiert.
Durch das Tag f:ajax, das in der Komponente inputText eingebettet ist, wird beim onchange-Trigger des Feldes ein AJAX-Request abgesetzt (siehe Abbildung 4). Dies ist das Default-Verhalten. Möchte man einen anderen Trigger des jeweiligen Feldes benutzen, so kann man dies mit dem Attribut event angeben.
Das execute-Attribut veranlasst die Abarbeitung des JSF-Lebenszyklus für die angegebenen Komponenten (exclusive der Rendering-Phase). In unserem Beispiel ist dies das Feld mit dem Tag f:ajax (@this) und das Feld mit der id=”vor”. Mit dem Attribut render gibt man an, welche Komponenten der Seite aktualisiert werden, in unserem Fall das Feld outputText mit der id=”voll”.
Auch dem commandButton haben wir ein AJAX-Tag verpasst. Hier wird dafür gesorgt, dass nach Drücken des Buttons das Ergebnis im Feld outputText mit der id=”email” neu umgesetzt und angezeigt wird (siehe Abbildung 4).
Man kann mit diesem Tag f:ajax nicht nur einzelne Komponenten mit AJAX-Funktionalität versehen, sondern auch ganze Regionen. Dafür reicht es aus, die gewünschten Komponenten in ein f:ajax-Tag einzubetten. Die Standard-Trigger der eingebetteten Komponenten werden dadurch mit AJAX-Funktionalität angereichert.
Die Validierung ist ein zentrales Thema jeder Business-Anwendung. Zudem kommt sie auch noch in verschiedenen Schichten vor.
Zunächst wird meist in der Oberfläche eine erste Validierung durchgeführt. Anschließend wird aus dem User Interface-Controller eine Business-Funktionalität aufgerufen. In dieser muss natürlich auch nochmals validiert werden. Zu guter Letzt wird eine Bean persistiert, was erneut eine Validierung voraussetzt.
In der Vergangenheit kamen auf den verschiedenen Layern zum Teil unterschiedliche Technologien zum Einsatz, was zu Redundanzen oder sogar zu unterschiedlichen Ergebnissen in der Validierung führte. Um diesen Diskrepanzen vorzubeugen, wurde erstmals eine standardisierte API (Bean Validation API) erarbeitet, mit der genau diese Anforderung umgesetzt werden kann. Die Validierungslogik ist an einer zentralen Stelle, nämlich an der Klasse selbst zu finden.
Diese Bean Validation API kann nun auch zusammen mit JSF benutzt werden. Allerdings müssen die dazu notwendigen Bibliotheken ggf. separat in das Projekt mit eingebunden werden. JSF 2.0 schaut beim Validieren von Managed Beans nach, ob an einer Eigenschaft Bean Validation API-kompatible Annotationen vorhanden sind und wertet diese aus.
Wir erweitern nun unser Beispiel um diese Annotationen (siehe Abbildung 5). Beide Eingabeattribute (vorname, nachname) dürfen nicht Null sein und der Nachname muss mindestens 2 Zeichen lang sein. Die Meldung, die beim Attribut message steht, wird im Fehlerfall auf der Oberfläche ausgegeben.
Ein großer Kritikpunkt an JSF 1.x war, dass das Erstellen eigener Komponenten keine triviale Sache war. In JSF 2.0 wird das Ganze sehr vereinfacht, dank der Integration von Facelets und der darin enthaltenenen Composite Components.
Eine Composite Component kann ohne viel Aufwand angelegt werden und somit die Wiederverwendbarkeit von Code Snipets in Form von Tags erreicht werden.
Entwickler erhalten durch die Verbindung von Facelets und Ressourcen die Möglichkeit, Komponenten aus beinahe beliebigen Seitenfragmenten aufzubauen. Eine Composite Component ist im Grunde nichts anderes als ein XHTML-Dokument, das in einer Ressourcenbibliothek abgelegt ist und die Komponenten deklariert.
Wie dies funktioniert, zeigen wir an unserem Beispiel, indem die Eingabe der Namen als eigene Composite Component erstellt wird und somit wiederverwendbar ist. Als erstes erstellen wir eine Komponente, die wir in der Datei vollerName.xhtml speichern.
Durch das neue Resource Loading in JSF 2.0 werden Komponenten (und Resource Bundles) automatisch erkannt, wenn sie im Verzeichnis /resources/<Name der Komponentenbibliothek> abgelegt sind. Wir speichern also unsere Datei vollerName.xhtml im Verzeichnis /resources/meineKomponenten ab. Die Komponente steht dann ohne weitere Konfiguration zur Verfügung.
Eine Komponente besteht immer aus einer Schnittstelle (Interface) und der Implementierung. In unserem Beispiel (siehe Abbildung 6) hat unsere Schnittstelle (dargestellt durch das Tag <composite:interface>) zwei Attribute: vName und nName. In der Implementierung (dargestellt durch das Tag <composite:implementation>) kann auf die übergebenen Attribute über die vordefinierte cc-Variable zugegriffen werden (in unsersem Fall cc.attrs.vName und cc.attrs.nName).
Die Verwendung unserer neuen Komponente ist ebenfalls sehr einfach. Zunächst muss die neue Bibliothek in den Namespace aufgenommen und mit einem Präfix (in unserem Fall oka) verküpft werden (siehe Abbildung 7). Danach kann die Komponente über das Tag oka:vollerName verwendet werden.
Zum Abschluss noch eine kleine, aber trotzdem sehr wichtige Neuerung. Ein häufiges Szenario in Anwendungen ist es, Url-Parameter einlesen zu müssen. Das ist meist dann der Fall, wenn eine Anwendung von einer anderen Anwendung aus aufgerufen wird und Übergabewerte als Url-Parameter übergeben werden, z. B. localhost:8080/meinProgramm/eingabe.jsf?nachname=Kaluza.
In JSF 1.2 war es prinzipiell zwar möglich, auf diese Parameter zuzugreifen, allerdings war dieser Zugriff ein wenig „hacky“. Man musste über den ExternalContext gehen und die gesamte Syntax sah wenig elegant aus.
Hier kommt in JSF 2.0 das Tag f:viewParam zum Tragen. Mit Hilfe dieses Tags wird der übergebene Parameter automatisch ausgelesen und wie in unserem Beispiel gezeigt direkt in der Managed Bean mitarbeiterBean gespeichert (siehe Abbildung 8). Auf diesen übergebenen Wert wird die Datenkonvertierung und Datenvalidierung angewendet.
Kommt es bei der Übergabe zu einem Fehler (ist der Wert z. B. keine 2 Zeichen lang, wie in unserem Beispiel gefordert), so wird eine entsprechende Fehlermeldung ausgegeben.
Dieser Artikel konnte viele Dinge nur anreißen und einige Neuerungen konnten auch gar nicht erwähnt werden. Dennoch wurde deutlich, dass es sich bei JSF 2.0 um eine konsequente Weiterentwicklung des bisherigen Standards handelt und dieser nun entgültig erste Wahl ist, wenn es um einfache und effiziente Web-Entwicklung mit Java geht.