Natürlich lassen sich eigene Validatoren bzw. Konverter über das Implementieren der Validator- bzw. Converter-Schnittstelle erstellen. Es ist auch möglich Validatoren direkt innerhalb einer Controller-Klasse zu schreiben, dieses widerspricht aber dem Trennungsansatz und daher sollte darauf verzichtet werden.
Im folgenden soll anhand zur Überprüfung von Emailadressen ein Validator erstellt werden. Dazu wird eine Java-Klasse erzeugt, die das Interface Validator implementiert. Die vorgegebene Methode ist:
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
Man erhält also sowohl den aktuellen Context FacesContext als auch die zu überprüfende Komponente UIComponent und den aktuellen Wert value der Komponente. Im Fehlerfall wird eine ValidatorException ausgelöst mit Angabe der Fehlernachricht FacesMessage.
Vgl. dazu Beispiel /src/util/validator/Mail.java
Der so erstellte Validator muss dann in die Konfiguration eingetragen werden:
<validator>
Vgl. dazu Beispiel /Webroot/WEB-INF/tutadmin/faces-config.xml
<validator-id>MailValidator</validator-id>
<validator-class>
util.validator.Mail
</validator-class>
</validator>
Und kann dann an eine Komponente angehängt werden mittels dem <f:validator/>
-Tag:
<h:inputText id="email" value="#{MemberControllerBean.currentMember.email}" required="true" maxlength="50">
Vgl. dazu Beispiel /Webroot/tutadmin/member_edit.jsp
<f:validator validatorId="MailValidator"/>
</h:inputText>
(Quelle: Bosch, Andy – um JSF EL Funktionalität erweitert)
Es soll eine Komponente entwickelt werden, die sowohl grafisch als auch in Worten eine Prozentangabe ausgibt. Die Komponente soll UsageBar
heißen und der hinterlegte Wert soll in inuse
gespeichert werden. Dabei wurde die Komponente erweitert, so dass sie auch mit Ausdrücken der JSF EL benutzt werden kann.
Der Komponenten Tag-Handler wird angesprochen, wenn ein Tag verwendet wurde. Während der Apply Request Value-Phase wird in ihm der Stringwert der Attribute gespeichert. Weiter hin liefert der Tag-Handler Informationen über die Komponentenklasse getComponentType()
und über den zuständigen Renderer mittels getRendererType()
. Für die Klasse UsageBarTag
ist das Attribut inuse
einzurichten, das den anzuzeigenden Prozentwert enthält. Dabei muss aber beachtet werden, dass sowohl ein direkter (fester) Wert, aber auch ein Laufzeitwert, angegeben werden kann.
Vgl. dazu Beispiel /src/com/edu/jsf/bsp/tag/UsageBarTag.java
Der im Schritt zuvor entwickelte Tag-Handler wird mittels XML beschreiben, damit er auch in einer Seite verwendet werden kann.
Vgl. dazu Beispiel /WebRoot/WEB-INF/custom-jsf-tld
Alle eigenen Komponenten erben von UIComponentBase
, wobei spezieller auch von UIInput
, UIOutput
oder UICommand
geerbt werden kann, dementsprechend stehen dann weitere Funktionen zur Verfügung (z.B. Ereignisverarbeitung). Dieses erfordert dann das implementieren weiterer Schnittstellen. Generell übernimmt die Komponentenklasse die Validierung und Zustandsspeicherung sowie das Modellupdate und die Ereignisverarbeitung. Ebenso kann auch das Rendern in dieser Klasse erfolgen. Aus Demonstrationszwecken wurde hier aber eine separate Render-Klasse angelegt. Die Komponenten-Klasse UIUsageBar
stellt daher nur die Getter und Setter für inuse
bereit, wobei beim Get darauf zu achten ist, einen vorliegenden Value-Bind aufzulösen. Zusätzlich wird mittels saveState
und restoreState
das Speichern und Wiederherstellen des vorherigen Zustandes unterstützt. Bei serverseitiger Zustandsspeicherung werden diese Methoden nicht benötigt, aber bei clientseitiger Speicherung würde der zugeordnete Wert nicht mit gespeichert werden und entsprechend verloren gehen.
Vgl. dazu Beispiel /src/com/edu/jsf/bsp/tag/UIUsageBar.java
UsageBarRenderer
Renderer beerben die Klasse Renderer
und kümmern sich um die (De-)Codierung. Das Decodieren bedeutet in diesem Zusammenhang, dass Werte aus einem Request in die Komponente übernommen werden können. Beim Codieren werden drei Methoden zeitlich nacheinander abgearbeitet: encodeBegin
, encodeChildren
und encodeEnd
. Somit kann der Start, der Body, also die Kinder und schließlich das Ende der Komponente gerendert werden. Für die Prozentanzeige genügt es encodeBegin
zu verwenden, da kein Body akzeptiert wird.
Vgl. dazu Beispiel /src/com/edu/jsf/bsp/tag/UsageBarRenderer.java
Schlussendlich müssen Render- und Komponentenklasse nur noch in die Konfiguration eingetragen werden und können dann wie die JSF-Standard-Tags durch Angabe der TLD verwendet werden.
Vgl. dazu Ende von Beispiel /Webroot/WEB-INF/tutadmin/faces-config.xml
Bei Implementierung des PhaseListener-Interface müssen folgende drei Methoden angelegt werden:
public PhaseId getPhaseId()
Der Rückgabewert legt fest, ob eine bestimmte oder alle Phasen überwacht werden sollen. (Auswahl erfolgt durch Konstanten z.B PhaseId.ANY_PHASE)
public void beforePhase(PhaseEvent pE)
wird vor der Phase ausgeführt
public void afterPhase(PhaseEvent pE)
wird nach der Phase ausgeführt
Vgl. dazu Beispiel src/util/listener/AllPhaseListener.java
Mittels PhaseListenern ist es möglich Logging zu realisieren in dem die erwähnte PhaseId.ANY_PHASE
verwendet wird (dieses macht der Beispiel PhaseListener). Ein anderes Einsatzgebiet wäre das Kontrollieren von kritischen Parametern z.B. für Adminbefehle (vor PhaseId.RESTORE_VIEW
). Also eine Kontrolle, ob eine Zugriffsberechtigung wirklich vorliegt.
Der Standard Lebenszyklus kann komplett angepasst werden. So kann der komplette Ablauf ausgetauscht werden oder nur einzelne Elemente. Innerhalb der Restore View Phase kann der StateManager
zum Verwalten der Wiederherstellung und Speicherns sowie der ViewHandler
zum Verwalten der Sichten angepasst werden. Dazu werden die entsprechenden Klassen beerbt. Gleiches ist möglich, wenn man das Navigationsverhalten verändern bzw. erweitern will. Hierzu kann die Klasse NavigationHandler
erweitert werden. Wie schon erwähnt werden in der Render Response Phase alle Komponenten gerendert. Dazu können für einzelne Komponenten eigene Renderer erstellt werden oder für alle Komponenten komplette RendererKits angelegt werden.