Das Tutorial zur neuen Version von JSF

T-Shirts mit JavaServer Faces 2.2

Andy Bosch

Nach fast zwei Jahren Entwicklungs- und Beratungszeit steht eine neue Version von JavaServer Faces bereit. Mit Version 2.2 wurden einige lang erwartete Änderungen und Neuerungen in den Standard aufgenommen. Gerade während der letzten Monate ging es in der Expert Group nochmals sehr eifrig zur Sache, bis man die letzten Features für den Final Draft zusammen hatte. Und es hat sich durchaus gelohnt. In diesem Tutorial wird eine Webanwendung basierend auf JSF mit Schwerpunkt auf den neuen JSF-2.2-Funktionen gebaut.

JSF 2.2 wird durch den Java Specification Request (JSR) 344 beschrieben. Die Anfänge dieses JSRs liegen im Jahr 2011. Ursprünglich war man angetreten, diese neue Version innerhalb kürzester Zeit fertigzustellen. Allerdings war dieses Vorhaben so nicht realisierbar. Ein Standard sollte durchaus ausführlich und umfangreich diskutiert und evaluiert werden. Schließlich soll er langfristige (Planungs-)Sicherheit bieten und eine langfristige Orientierung vorgeben. Es hat daher sicherlich nicht geschadet, JSF 2.2 erst im Zuge von Java EE 7 zu veröffentlichen.

Das Vorhaben: T-Shirts verkaufen

Als ich diesen Artikel begonnen habe zu schreiben, wurde in der Presse ein bestimmtes T-Shirt-Logo eines Versandhändlers eifrig diskutiert: „In Mathe bin ich Deko“. Ohne politisch dazu Stellung nehmen zu wollen, fand ich die Idee gut, eine JSF-2.2-Beispielanwendung als T-Shirt-Shop zu entwickeln. Gerade für die neuen JSF-2.2-Features passt das sehr gut. Als kleiner Vorausgriff seien die Themen Faces Flow, File Upload, ViewActions, HTML5 und Resource Library Contracts genannt. Der Shop soll eine Liste von Basisshirts anbieten, von denen man sich eines auswählen und mit einem Logo versehen kann. Zusätzlich kann ein Text aufgedruckt werden. Der Shop soll ein modernes, HTML5-basiertes Design aufweisen.

Abb. 1: Der fertige T-Shirt-Shop

Die Features im Überblick

In verschiedenen Vorträgen und Artikeln wurde in den letzten Monaten regelmäßig über JSF 2.2 berichtet. Dabei wurde auch immer von den „Big Ticket Features“ gesprochen. Damit sind die großen Neuerungen gemeint, die Einzug in die Spezifikation gehalten haben. Die Anzahl, wie viele es letztlich sind, hat dabei bis zuletzt geschwankt. Folgende größeren Neuerungen haben es letztlich noch in die Spezifikation geschafft:

Resource Library Contracts: Hiermit soll erreicht werden, dass eine Website basierend auf unterschiedlichen Templates angezeigt werden kann. In der Praxis wird dabei häufig der Begriff des „Whitelabeling“ verwendet. Eine Website kann mithilfe von unterschiedlichen Templates und Style Sheets auf unterschiedliche Weise gerendert werden. Im CMS-Umfeld kennt man diese Templateansätze von Joomla oder anderen Systemen. Es sind separate View-Artefakte, die durch einfaches Deployment in die Anwendung übernommen werden können. Dieser Ansatz in JSF geht noch einen Schritt weiter als die bisherigen Facelets-View-Template-Konzepte. Es ist nun möglich, komplette Markup-Skelette (Template) zusammen mit Bildern, JavaScripts und Style Sheets als eigene Deployment-Unit zu bauen. Ein einfaches Einbinden einer JAR-Datei genügt, um dieses Template (der korrekte Begriff ist „Resource Library Contract“) in eine Anwendung aufzunehmen.

Faces Flows: In Anlehnung an bestehende Lösungen wie Spring Web Flow oder ADF Task Flows wurde ein Flow-Konzept umgesetzt, mit dem modular aufgebaute Seiten in Kombination mit Navigationsregeln gebaut werden können. Ein Anwendungsfall wäre z. B. der klassische Wizard beim Bezahlen in einem Shop. Es muss eine bestimme Seitenfolge eingehalten werden, nur fest definierte Sprünge zwischen den Seiten sind erlaubt.

Stateless Views: Ein großer Vorteil von JSF ist es häufig, stateful zu sein. Damit ist gemeint, dass der Zustand der View in der Regel serverseitig gespeichert und gehalten wird. Dieser stateful-Ansatz hat viele Vorteile, z. B. eine hohe Sicherheit in der Seitenbeschreibung. Es kann somit nicht einfach ein auf der Serverseite als disabled hinterlegtes Feld im Browser per Tamper Data als enabled gewandelt werden und ein Request-Parameter in das serverseitige Modell gelangen. Durch den stateful-Ansatz sind die Parameter der View gesichert gespeichert. Der stateful-Ansatz ist jedoch nicht immer notwendig und gewünscht. Daher ist es nun möglich, in einer View zu sagen, dass der (serverseitige) State nicht notwendig ist.

HTML5-friendly Markup: Es ist ja nicht so, dass JSF in der Vergangenheit zu HTML5 unfreundlich gewesen wäre. Dennoch bringt die neue Version hier entscheidende Verbesserungen in der Zusammenarbeit mit speziellen HTML5-Funktionen. Während in der Vergangenheit immer davon ausgegangen wurde, dass man Markup am besten komplett vor dem Entwickler versteckt und in teilweise komplexe JSF-Komponenten wrappt, findet hier eine kleine Revolution bzw. ein Umdenkprozess statt: Der Webentwickler hat wieder verstärkt Kontrolle über das Markup und kann mittels State-of-the-Art-JavaScript-Frameworks den DOM-Baum aktiv beeinflussen. Damit das Markup nicht nur von einer JSF-Komponente beeinflusst wird, sondern auch durch den Webentwickler noch manipuliert werden kann, gibt es hierfür so genannte PassThroughAttribute, die nicht nur in HTML5, aber eben speziell dort sinnvoll sind.

Noch einiges mehr: Neben den aufgezählten Änderungen bietet JSF 2.2 aber noch eine Reihe weiterer Verbesserungen. Kurz erwähnt sind dies: die CDI-basierte ViewScoped-Annotation, die Tatsache, dass UIData Collection-Interface unterstützt, ViewActions, Delay-Option bei Ajax Calls, Kompositkomponenten und Nicht-Komposits in einer TagLib und noch viele Kleinständerungen.

Aufsetzen des Projekts

Zum Zeitpunkt der Entstehung dieses Artikels ist das Aufsetzen einer Umgebung noch ein wenig umfangreicher, da es doch noch ein paar Ungereimtheiten in der Implementierung und dem Zusammenspiel mit verschiedenen Containern gibt. Doch das sollte sich in den nächsten Wochen erledigen. Ich empfehle für erste Tests, GlassFish 3.1.2 (stable) oder einen GlassFish 4 Build > 81 zu verwenden. Die aktuelle JSF-Referenzimplementierung Mojarra kann über das java.net Repository bezogen werden [1]. Für ein Update des GlassFish auf die aktuelle JSF-2.2-Implementierung verweise ich auf einen Blogeintrag von Ed Burns [2]. Darin beschreibt er, an welcher Stelle JAR-Files auszutauschen sind und in welchen Dateien eine Änderung vorzunehmen ist. Zeitaufwand: rund fünf Minuten. Das Projekt selbst ist als klassisches Webprojekt aufgesetzt, die Seiten wurden als XHTML-Seiten mit einem Mastertemplate erstellt. Eine faces-config.xml steht bereit (auch wenn sie seit JSF 2.0 optional ist). Die üblichen Eintragungen in der web.xml wurden vorgenommen. Das gesamte Projekt für den ersten Teil des Artikels kann unter [3] heruntergeladen werden.

Pageflow

In Abbildung 2 ist ein Pageflow skizziert. Der Shop ist nicht allzu umfangreich, er soll ja lediglich die neuen Features von JSF 2.2 verdeutlichen. Es gibt eine zentrale Home-Startseite, eine Produktseite, die eine Liste mit möglichen Shirtvarianten enthält, sowie eine Impressumseite. Von der Produktseite kann ein Bestell-Wizard gestartet werden, der neben der Rechnungs- und Lieferanschrift auch die Details der Bestellung (Logo, Schriftzug) entgegennimmt. Dieser Bestell-Wizard ist momentan über die üblichen Navigationsregeln verbunden. An dieser Stelle werden wir in der nächsten Magazinausgabe auf Faces Flows eingehen.

Abb. 2: Pageflow der Anwendung

Erstellung der Seiten

Bei der Erstellung der XHTML-Seiten soll natürlich ein Templating verwendet werden. Dieses Feature existiert schon seit JSF 2.0 bzw. in den Facelets-Vorgängerversionen. View-Templates ermöglichen es, ein View-Grundgerüst einer Webanwendung zu definieren, das für alle einzelnen XHTML-Seiten angewendet werden kann. Neu in JSF 2.2 sind Resource Library Contracts. Diese gehen noch einen Schritt weiter: Sie kombinieren das XHTML-Markup mit Style Sheets und Bildern in einem gesammelten Ordner, der als ein Paket einer Anwendung hinzugefügt werden kann. Dadurch wird ein sehr eleganter Templateansatz gefahren, mit dem durch einfaches Deployment von JAR-Dateien das gesamte Aussehen sowie der Markup-Aufbau ausgetauscht werden kann. Um dies zu realisieren, muss entweder ein Ordner/contracts im Content-Root des WAR-Files oder innerhalb eines META-INF-Verzeichnisses eines inkludierten JAR-Files vorhanden sein. Darunter liegen Ordner, die wiederum das Template samt zusätzlicher Artefakte enthalten. Die Spezifikation spricht hier von Declared Templates und Declared Resources. Diese Declared Templates sind wie übliche View-Templates aufgebaut und weisen „Lücken“ auf, in denen Templateclients ihre spezifischen Inhalte einbringen können (so genannte Declared Insertion Points). Die Struktur ist in Abbildung 3 zu sehen. Das verwendete Declared Template der Shirtapplikation ist auszugsweise in Listing 1 zu sehen.

Abb. 3: Verzeichnisstruktur der Contracts

Listing 1: Template in einem Resource Library Contract
<html ...>
       <h:head>
     <h:outputStylesheet name="global-styles.css" />
   </h:head>
  <h:body>
  <div id="wrap">
           ...[weitere Markup Angaben ] ...
    <div id="content">
      <ui:insert name="content" />
      <h:messages />
    </div>
  </div>
</h:body>
</html>

Zu Demonstrationszwecken habe ich in der gleichen Webapplikation zwei Resource Library Contracts angelegt, die ein unterschiedliches Aussehen erzeugen (contractCompanyBlue und contractCompanyRed). Da es natürlich nicht sinnvoll ist, zwei konkurrierende Contracts aktiv zu haben, muss durch eine Regel entschieden werden, welcher angezogen werden soll. Dies kann durch ein entsprechendes Mapping in der faces-config.xml erfolgen.

Listing 2: Mapping von URL-Patterns auf ein Contract
<application>
    <resource-library-contracts>
      <contract-mapping>
        <url-pattern>*</url-pattern>
        <contracts>contractCompanyBlue</contracts>
      </contract-mapping>
    </resource-library-contracts>
  </application>

Im Beispiel in Listing 2 habe ich alle Seiten auf den Contract contractCompanyBlue gemappt. Es könnten jedoch unterschiedliche Contracts aktiv sein und durch verschiedene Mappings je nach Bedarf angezogen werden. Es wäre jetzt auch möglich, den Contracts-Ordner aus einem JAR-File zu beziehen. Das hätte den Charme, dass durch ein einfaches Austauschen der JAR-Datei ein komplett neues Look and Feel ermöglicht wird.

Somit können Templates z. B. von einer externen Designagentur erstellt und durch einfaches Deployment in die Anwendung übernommen werden. Natürlich ist eine Einigung über den Contract (Wie heißen die Insertion Points? Wie ist der Name des Templates?) im Vorfeld notwendig.

Aufmacherbild: Magnifying optical glass with words tutorial on digital background von Shutterstock / Urheberrecht: Maksim Kabakou

[ header = T-Shirts mit JavaServer Faces 2.2 – Teil 2 ]

Verwendung von HTML5-Erweiterungen

Mittels JSF können sämtliche HTML-Varianten unterstützt werden. Grundlage ist die XHTML-Seite, die von JSF serverseitig verarbeitet wird. Das Resultat gelangt als HTML zum Browser. Mit JSF 2.2 wird künftig standardmäßig der Doctype für HTML5 generiert, wenn nichts anderes in den Views hinterlegt ist. Doch das ist natürlich noch lange nicht alles, was JSF 2.2 zum Thema HTML5 zu bieten hat. Nehmen wir unsere Anforderung, dass wir im Bestellprozess eine E-Mail-Adresse sowie die Farbe des T-Shirts und eine Menge angeben möchten. Für alle Eingabetypen, E-Mail, Menge und Farbe gibt es in HTML5 Standardeingabefelder bzw. ein Standardverhalten. Konkret soll ein

<input name="email" type="email" />
<input name="no" min="1" max="10" step="1" type="number" />
<input name="favcolor" type="color" />

erzeugt werden. Nicht alle Browser können dies schon interpretieren. Die meisten erzeugen einfach Standardeingabefelder. Mit Chrome sieht das Ergebnis dagegen wie in Abbildung 4 aus. Selbst eine Validierung einer Eingabe wird teilweise von Browsern schon übernommen, ohne dass dazu eigenes JavaScript notwendig wäre.

Abb. 4: HTML5-Elemente in Chrome

Doch wie können wir das Type-Attribut mittels eines <h:inputText>-Tags erzeugen? Hier sieht die JSF-2.2-Spezifikation so genannte Pass Through-Attribute vor. Damit können in JSF-Tags weitere Attribute mit aufgenommen werden, die innerhalb der Kombination von UI-Komponente und UI Renderer nicht bekannt sein müssen und nur durchgeschleust werden. Daher auch der Name „Pass Through“. Somit ist die Definition der Eingabefelder wie in Listing 3 möglich. Ich habe an dieser Stelle zwei unterschiedliche Syntaxmöglichkeiten verwendet. Man kann ein PassThroughAttribute über den neuen Namespace p: direkt im Tag verwenden, oder nested mit dem Tag <f:passThroughAttribute>.

Listing 3: Verwendung von PassThrough-Attributen
<html ...
   xmlns:p="http://java.sun.com/jsf/passthrough">
      <h:inputText p:type="number" p:min="1" p:max="10" 
             p:step="1" value="#{shirtOrder.amount}" />

<h:inputText id="col" value="#{shirtOrder.color}">
   <f:passThroughAttribute name="type" value="color" />
</h:inputText>

Doch JSF 2.2 geht noch einen Schritt weiter: Statt ein JSF-Tag zu verwenden und diesem zusätzliche Attribute zu geben, kann man das Ganze auch anders herum angehen: mit Pass Through-Elementen. Damit kann der Seitenautor beliebige (HTML5-)Tags verwenden, ohne an die Reglementierungen der JSF-Tags gebunden zu sein. Um dennoch die Vorteile von JSF zu genießen, kann dem HTML-Tag eine Komponente zugewiesen werden.

<input type="number" pattern=" [0-9]* " jsf:value="#{bean.anzahlShirts}" />

Diesen Ansatz kann man natürlich viel weiter treiben. In Listing 4 ist eine Seite im Bestellprozess zu sehen, die komplett auf HTML basiert und lediglich über das jsf:-Attribut eine Verbindung zu JSF aufweist.

Listing 4: „Pass Through“-Elemente
<form jsf:prependId="false">
  Vorname: 
  <input type="text" jsf:value="#{customer.firstname}" jsf:id="first" />
  <h:message for="first" />
   
  Nachname:
  <input type="text" jsf:value="#{customer.lastname}" jsf:id="last" />
  <h:message for="last" />

  <input type="submit" jsf:value="Weiter" jsf:action="buy-details.xhtml" />
</form>

Damit kann diese Seite auch direkt in einem Browser getestet werden – ohne Container. Es wäre hier möglich, Webdesigner oder JavaScript-Entwickler aktiv in die Seitenentwicklung einzubinden, ohne die komplexen Abläufe in JSF verstehen zu müssen. Natürlich wird so auch dem (neuen) Paradigma Rechnung getragen, dass der Webentwickler (und nicht nur der Designer) verstärkt mit dem Markup arbeiten muss. Damit hat man sämtliche Freiheiten, die HTML(5) bietet. Dennoch kann man den serverseitigen Anteil von JSF mitnutzen. Voilà.

Laden der Produkte

Sobald ein Besucher auf die Seite produkte.xhtml navigiert, soll eine Liste der möglichen Shirts angezeigt werden. Wir unterstellen, dass diese Liste pro Benutzer geladen wird und im ViewScope abgelegt werden soll. Eventuell wird aufgrund der Lagerverfügbarkeit das Sortiment aufgebaut oder aufgrund der Benutzereinstellungen. Der Grund sei hier jedoch egal – es geht darum, ein Bean durch einen Trigger zu einer Aktion zu veranlassen.

Listing 5: CDI Bean für die Shirtliste
@Named
@ViewScoped
public class ShirtContainer implements Serializable {
  
  private static final long serialVersionUID = -3065151806352064236L;
  private Collection<Shirt> shirts;

  public void setShirts(List<Shirt> shirts) {
    this.shirts = shirts;
  }

  public Collection<Shirt> getShirts() {
    return shirts;
  }
  
  public void addShirt( Shirt s ) {
    if ( shirts==null ) {
      shirts = new ArrayList<Shirt>();
    }
    shirts.add( s );
  }
}

Die Liste der möglichen T-Shirts ist eine einfache CDI Bean (Listing 5). Es sei an dieser Stelle explizit erwähnt, dass die Spezifikation mit JSF 2.2 sehr stark auf eine Integration mit CDI achtet. Es ist angeraten, die bislang gültigen @ManagedBean-Anweisungen nicht mehr zu verwenden, da sie vermutlich in künftigen Versionen auf deprecated gesetzt werden. Auch die neuen Features von JSF 2.2 bauen ausschließlich auf CDI auf. In der Klasse ShirtContainer ist eine extrem wichtige Kleinigkeit zu finden. Ich habe mich für die Annotation @ViewScoped entschieden. Dies ist jedoch nicht der ViewScope aus früheren JSF-Versionen (Package javax.faces.bean), sondern ein neuer CDI Custom Scope aus dem Package javax.faces.view!

Für Aktionen, die vor dem Anzeigen einer Seite geschehen sollten, kannte man in JSF 2.0 nur die Möglichkeit, ein PreRenderView-Event abzufangen und in der Bearbeitung dieses Events ggf. Beans zu befüllen. JSF 2.2 bietet eine verbesserte Möglichkeit über ViewActions. Diese weisen einer Seite eine direkte Aktion zu, die garantiert vor dem Rendering der Seite ausgeführt wird. Dieses Feature zielt allerdings auf GET-Requests, bei POSTs werden Viewactions im Standardfall nicht ausgewertet:

<f:metadata>
  <f:viewAction action="#{dataSourceSimulator.loadProducts}" />  
</f:metadata>

Doch worin unterscheiden sich ViewActions nun von einem PreRenderView-Event? ViewActions sind dazu bestimmt, Aktionen durchzuführen, die weit vor dem Rendern der Seite selbst geschehen. Zu diesem Zeitpunkt ist eventuell noch nicht einmal klar, ob die Seite überhaupt angezeigt werden soll oder auf eine andere Seite geleitet wird. Security ist somit ein möglicher Ansatzpunkt. Daher kann über einen Rückgabewert in einer ViewAction auch die anzuzeigende View gesteuert werden. Bei PreRenderView-Events ist dies nur über sehr große Umwege möglich. Ein PreRenderView-Event wird zudem in der Render-Phase ausgewertet, also zu einem Zeitpunkt, zu dem der komplette Komponentenbaum schon aufgebaut ist. ViewActions werden ausgewertet, bevor der komplette Baum hergestellt ist. In unserem konkreten Fall des Shirtshops wäre somit auch ein PreRenderView-Event möglich gewesen. Würden wir jedoch zusätzliche Rollen- und Rechteprüfungen einbauen oder gar aufgrund von Request-Parametern (verschiedene Einstiege mittels des URLs) die Navigation beeinflussen, wäre eine ViewAction durchaus die bessere Alternative.

Der Vollständigkeit halber sei noch die Auswahl eines Shirts erwähnt. Jede Tabellenzeile hat einen <h:commandButton>, der auf eine entsprechende Aktionsmethode im View Controller verweist. Die action dieses Buttons lautet auf #{shopCtrl.selectProduct(current)}. Es wird über die Expression Language ein weiterer Parameter (das ausgewählte Shirt) übergeben. Somit hat die JSF-Aktionsmethode einen Parameter als Argument. Dies ist kein spezielles JSF-2.2-Feature. Diese Möglichkeit bestand schon in früheren Versionen, setzte allerdings das Vorhandensein einer neuen Version der EL voraus. Da dieses nette Feature jedoch nicht allzu bekannt ist, habe ich es nochmals explizit erwähnt.

Einbau eines File Uploads

Neben einem provokanten Spruch auf dem T-Shirt möchte der Nutzer eventuell noch eine Grafik abbilden oder ein Foto aufdrucken lassen. Wir möchten im Bestellprozess somit eine Möglichkeit schaffen, eine (Grafik-)Datei hochzuladen. Hier bietet JSF 2.2 endlich die lange erwartete File-Upload-Komponente an. Ein File Upload ist an sich nichts spektakulär Neues, aber anstelle von vielen unterschiedlichen Lösungsansätzen in diversen Zusatzprojekten ist es nun endlich im Standard festgelegt. Damit können sich unterschiedliche Komponentenbibliotheken auf einen zentralen Weg festlegen, ohne dass jedes Projekt eine eigene Lösung anstrebt. Das neue Tag <h:inputFile> bietet die Möglichkeit, beliebige Dateien zum Server hochzuladen und dort zu validieren und zu verarbeiten. Zu beachten ist, dass im umgebenden Formular ein Enkoding vom Typ multipart/form-data angegeben wird. Hervorzuheben ist, dass die File-Upload-Komponente vollständig in die übliche JSF-Verarbeitung integriert ist. Es können Validatoren angegeben und Fehlermeldungen dafür erzeugt werden. In unserem Beispiel wird eine Validierungsmethode verwendet, die lediglich Grafiken vom Typ .png akzeptiert. Die Bestellseite mit dem Dateiupload sieht daher folgendermaßen aus:

<h:inputFile id="file"  value="#{shirtOrder.template}">
   <f:validator validatorId="uploadPngValidator" />
   </h:inputFile>
<h:message for="file" />

Die serverseitige Verarbeitung hat streng genommen nichts mit JSF zu tun. Im Managed Bean wird ein Objekt vom Typ javax.servlet.http.Part gespeichert. Darüber sind sowohl die Nutzdaten als auch die Metadaten auszulesen. Im beigefügten Projekt habe ich einige zusätzliche Utility-Methoden verwendet, die das Auslesen der Metadaten vereinfachen. Neben der klassischen Variante, die Datei bei einem Form-Post hochzuladen, kann auch ein Ajax-Upload realisiert werden. Hier wurde eine sehr schöne Integration in die bereits vorhandenen Konzepte vorgenommen. Sie können für die Ajax-Variante ein Nested-Tag <f:ajax /> verwenden, und schon funktioniert der Upload über Ajax.

Zwischenfazit des ersten Teils

In diesem ersten Teil des JSF-2.2-Tutorials haben wir einige neue Features kennengelernt. Unsere auf JSF 2.2 basierende Anwendung ist deutlich eleganter als mit den bisherigen 2.0-Funktionen. Dennoch gibt es ein paar Stellen, an denen wir weiter optimieren können. Als Beispiel sei der Pageflow im Bestellprozess genannt oder grundsätzlich auch das Thema Security. Das sind Themen, die im nächsten Teil behandelt werden. Auch unbeantwortet ist die Frage, ob unsere Anwendung Multi-Tab-fähig ist bzw. ob wir hierfür etwas Besonderes tun müssen. Seien sie somit gespannt, welche weiteren neuen Funktionen in JSF 2.2 noch vorhanden sind.

Geschrieben von
Andy Bosch
Andy Bosch
Andy Bosch (andy.bosch@jsf-academy.com) 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.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.