Professional Documents
Culture Documents
I - Introduction..............................................................................................................................................................3 II - Le cycle de vie d'une requte JSF........................................................................................................................3 II-A - Restore View................................................................................................................................................. 4 II-B - Apply Request Values...................................................................................................................................4 II-C - Process Validations.......................................................................................................................................5 II-D - Update Models Values..................................................................................................................................5 II-E - Invoke Application......................................................................................................................................... 6 II-F - Render Response......................................................................................................................................... 6 III - Les messages....................................................................................................................................................... 6 III-A - Manipuler les messages.............................................................................................................................. 7 III-B - Afficher des messages.................................................................................................................................7 IV - Les converters...................................................................................................................................................... 9 IV-A - Principes...................................................................................................................................................... 9 IV-B - Les converters standards............................................................................................................................ 9 IV-C - Dvelopper un converter........................................................................................................................... 11 IV-D - Passer outre les erreurs de conversions.................................................................................................. 13 V - Les validators.......................................................................................................................................................14 V-A - Principes..................................................................................................................................................... 14 V-B - Les validators standards.............................................................................................................................14 V-C - Dvelopper un validator............................................................................................................................. 15 V-D - Passer outre les erreurs de validations..................................................................................................... 17 VI - Dvelopper un composant JSF.......................................................................................................................... 17 VI-A - Principes.................................................................................................................................................... 17 VI-B - Le composant UIPager..............................................................................................................................17 VI-C - Le tag pager.............................................................................................................................................. 19 VI-D - Utiliser le composant UIPager...................................................................................................................20 VI-E - Ajout d'attributs.......................................................................................................................................... 21 VI-F - Gestion des actions de l'utilisateur............................................................................................................24 VI-G - Le renderer................................................................................................................................................26 VII - Conclusion......................................................................................................................................................... 28
-2Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
I - Introduction
Nous avons vus comment construire des pages JSF simples dans le tutoriel prcdent. Cependant, ces notions sont insuffisantes pour dvelopper une application de gestion complte. (Voir le tutoriel : http://schmitt.developpez.com/ tutoriel/java/jsf/introduction/) Lors de la saisie de donnes par l'utilisateur il est souvent indispensable de valider et ou convertir les valeurs saisies avant de les injecter dans un JavaBean . Dans le cas de dveloppements importants, il est souvent utile de crer des composants rutilisables pour augmenter la productivit et faciliter la maintenance. Ce tutoriel vous montrera comment complter vos connaissances pour dvelopper une application de gestion complte avec JSF. Remarque: Ce tutoriel s'appuie sur le tutoriel prcdent en ce qui concerne les exemples de code. Il est donc fortement recommand de lire le tutoriel prcdent.
-3Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Cette page est destine un client web qui supporte le langage HTML. Une page HTML est un fichier texte qui contient un assemblage de balises spcifies par la norme HTML. Cependant, nous avons vu que JSF utilise un arbre d'objets pour reprsenter une vue. La racine de cet arbre est une instance de la classe UIViewRoot. Il s'agit d'une structure de donnes bien diffrente d'un fichier texte. Cette phase va donc consister reconstituer l'arbre de composants qui correspond la vue HTML de l'utilisateur qui soumet la requte. L'implmentation JSF utilise les informations contenues dans l'objet HttpServletRequest pour dterminer le bon algorithme : Si la vue est (hello-world.jsp) est demande pour la premire fois, il faut crer une instance de UIViewRoot et lui associer un nom (souvent le nom de la page JSP). Si la vue existe dj pour JSF alors l'arbre de composants correspondant devient la vue courante.
Ceci implique que JSF mmorise l'arbre de composants, ce qui revient srialiser un objet de type UIViewRoot. Cette srialisation peut se faire du ct client (un champs cach dans un formulaire) ou du ct serveur. La mthode UIComponent.restoreState() est appele rcursivement sur l'arbre de composants. Elle rtablit l'tat du composant partir de l'tat sauv lors de la srialisation. Lorsque JSF a dtermin la vue courante, celle-ci est accessible via la classe FacesContext.
FacesContext facesContext = FacesContext.getCurrentInstance(); UIViewRoot viewRoot = facesContext.getViewRoot();
Ensuite JSF tablit le binding pour tous les composants concerns. (voir chapitre 8 de Introduction JSF)
A cet effet, la classe UIComponent qui est la classe anctre de tout composant JSF dispose de la mthode UIComponent.processDecodes(). Cette mthode est appele rcursivement sur chaque composant de l'arbre. Les composants ont la responsabilit de dcoder les informations qui les concernent. Le plus souvent, cela se traduit par la recherche d'une information dans les valeurs de la requte HTTP courante. Tout composant a un client id (un identifiant client voir l'attribut id dans la page HTML), si un paramtre identifi par le client id du composant existe dans l'objet HttpServletRequest alors le composant s'empare de la valeur associe et effectue ce que bon lui semble.
/** * Exemple de composant */ public class DummyComponent extends UICommand { public void processDecodes(FacesContext facesContext){ String clientId = getClientId(facesContext); Map paramMap = facesContext.getExternalContext().getRequestParameterMap(); if(paramMap.containsKey(clientId)){ Object value = paramMap.get(clientId); // Faire quelque chose pour changer mon tat } }
Notez bien que le seul le composant est ventuellement mis jour ici.
-5Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Si une erreur survient lors de la validation alors JSF saute la phase RenderResponse. JSF offre un modle vnementiel qui permet de dtecter les changements du modle. L'appel de la mthode UIComponent.processUpdates() peut poster des vnements.
-6Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Le code prcdent cre un message. Ce code n'est pas suffisant il faut ajouter le message au contexte JSF courant afin que les composants graphiques de message puisse l'afficher. La classe FacesContext possde une mthode qui permet cela :
FacesMessage facesMessage = new FacesMessage(); facesMessage.setSeverity(FacesMessage.SEVERITY_INFO); facesMessage.setSummary("Un rsum de message") facesMessage.setDetail("Dtail du message"); FacesContext facesContext = FacesContext.getCurrentInstance(); facesContext.addMessage(null,facesMessage);
Vous remarquerez que FacesContext.addMessage() possde deux paramtres. Le premier paramtre permet d'attacher un message un composant particulier, dans ce cas, le paramtre a pour valeur l'identifiant du composant (attribut id sur les tags JSF). Si l'id est null alors ce message est global.
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT <core:facet name="header"> <core:verbatim>Prnom</core:verbatim> </core:facet> <html:outputText value="#{customer.forname}"/> </html:column> </html:dataTable> <br> <html:commandButton value="Supprimer les clients" action="#{bankCtrl.removeSelectedCustomers}"/> <html:commandButton value="Ajouter un client" action="#{bankCtrl.addCustomer}"/> </html:form> </core:view>
Nous allons ajouter une zone de message au-dessus de la liste. Pour ce faire, JSF propose deux tags ddis l'affichage des messages : <html:messages> et <html:message>.
<%@ page contentType="text/html" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="html" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="core" %> <core:view> <html:form> <html:messages showDetail="true"/> <html:dataTable binding="#{bankCtrl.view.dataTable}" .... </core:view>
Il nous faut maintenant coder l'ajout de message dans le contexte JSF. Ici, c'est la responsabilit du Controller que d'assumer la cinmatique des oprations d'ajout et de suppression.
package com.facestut.mvc; import com.facestut.bean.Bank; import com.facestut.bean.Customer; public class BankListController implements Controller { ... public void removeSelectedCustomers(){ Bank bank = (Bank) getModel().getDatas(); Collection selectedObjects = getView().getSelectedObjects(); bank.getCustomers().removeAll(selectedObjects); String summary = "Les clients ont t supprims."; String detail = selectedObjects.toString(); addMessage(null,summary,detail,FacesMessage.SEVERITY_INFO); } public void addCustomer(){ Bank bank = (Bank) getModel().getDatas(); Customer customer = new Customer(); customer.setName("Nouveau"); customer.setForname("client"); bank.getCustomers().add(customer); String summary = "Client ajout."; String detail = "(" + customer.getName() + ")"; addMessage(null,summary,detail,FacesMessage.SEVERITY_INFO); } public void addMessage(String id,String summary,String detail,FacesMessage.Severity severity){ FacesContext facesContext = FacesContext.getCurrentInstance(); FacesMessage facesMessage = new FacesMessage(); facesMessage.setSeverity(severity); facesMessage.setSummary(summary); facesMessage.setDetail(detail); -8Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT facesContext.addMessage(id, facesMessage);
Nous verrons dans les chapitres suivants comment utiliser le tag <html:message>.
IV - Les converters
Les converters concernent les composants qui publie une de valeur. Ces composants implmentent les interfaces ValueHolder. Par exemple, UIOutput est le composant anctre de ce type de composants. Les converters sont galement utiliss lors de la saisie de valeurs.
IV-A - Principes
La conversion se dclenche lors de la phase ProcessValidation. Tout d'abord JSF rcupre la valeur soumise par le client et la stocke dans l'atrribut submittedValue (interface EditableValueHolder). Ensuite, cette valeur qui est une chane de caractres (HTTP ne connat que ce type de donnes) est convertie dans le type adquat puis stocke dans l'attribut localValue du composant. JSF choisit un converter par dfaut lorsque la valeur du modle est un type primitif (integer, float, .., ). Cependant, pour affiner cette conversion ou pour les types non primitifs il faut spcifier un converter.
Ensuite, les deux converters sont manipulables par le biais de deux tags : convertNumber et convertDate. Reprenons notre exemple de formulaire de saisie (chapitre 5 et 6 de Introduction JSF):
<%@ page contentType="text/html" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="html" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="core" %> <core:view> -9Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT <html:form> ... <html:outputText value="Total : "/> <html:outputText value="#{accountDatas.total}"> <core:convertNumber type="currency" currencyCode="EUR" minFractionDigits="3"/> </html:outputText> <BR/> <html:outputText value="Montant :"/> <html:inputText id="amount" value="#{accountDatas.amount}"> <core:convertNumber integerOnly="true"/> </html:inputText> ... </core:view>
Le tag spcifie quel est le converter que JSF utilisera lors de l'tape de conversion. Ce tag s'insre dans un tag qui construit un composant de type ValueHolder. Insrer un tag converter dans un tag qui ne remplit pas cette contrainte n'a aucun sens et provoquera une erreur. Ajoutons maintenant un attribut de type Date dans le bean puis un converter dans la page.
package com.facestut.bean; import java.util.Date; public class AccountDatas { ... // Ne pas oublier le get et le set private Date lastModified = new Date(); ...
Puis :
<%@ page contentType="text/html" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="html" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="core" %> <core:view> <html:form> <BR/> <html:outputText value="Nom : #{accountDatas.customer.name}"/> <BR/> <html:outputText value="Numro de compte : #{accountDatas.number}"/> <BR/> <html:outputText value="Date de MAJ : "/> <html:outputText value="#{accountDatas.lastModified}"> <core:convertDateTime pattern="MM/yy"/> </html:outputText> <BR/> <html:outputText value="Total : "/> ...
- 10 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Ces exemples sont valables en saisie. Lorsque JSF ne peut convertir la valeur saisie par l'utilisateur alors la page courante est affich de nouveau. En fait, JSF alors en phase ProcessValidation, saute directement la phase RenderResponse. Comme la phase UpdateModelValues est saute le modle n'est pas modifi. Cela empche la mise jour du modle avec des donnes non valides. Pour que l'utilisateur comprenne il est utile d'afficher le message d'erreur lanc par JSF. Reprenons notre exemple et saissisons une suite de lettre en lieu et place du montant. Cliquons sur Valider . JSF rafrachit la page mais rien n'indique que quelque chose s'est mal droul. Nous allons utiliser le tag message pour afficher l'erreur li au champs Montant :
... <BR/> <html:outputText value="Montant :"/> <html:inputText id="amount" value="#{accountDatas.amount}"> <core:convertNumber integerOnly="true"/> </html:inputText> <html:message for="amount"/> ...
Ainsi, le message d'erreur indique qu'il y eu une erreur de conversion. Par dfaut, JSF utilise la locale anglaise. Pour changer il faut la spcifier la locale dans le faces-config.xml. (voir la dtd et les codes des locales)
Object getAsObject(FacesContext,UIComponent component,String) : convertit la valeur soumise par le client (paramtre String) en Object String getAsString(FacesContext,UIComponent component,Object) : conevrtit l'objet du modle en String
La premire est appele dans la phase ProcessValidation et la deuxime dans la phase RenderResponse. Nous allons maintenant dvelopper un converter custom. Changeons un peu le fonctionnel de notre application. Un numro de compte devra maintenant avoir la structure suivante : CODEPAYS_CODEAGENCE_NUMEROCLIENT
- 11 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
CODEPAYS : 3 caractres CODEAGENCE: 5 chiffres NUMEROCLIENT : 6 chiffres _ : un caractre d'espacement Toute valeur qui est structur de la sorte est un numro de compte. Exemple : FRA 00005 123456 Dvelopper un convertisseur de numro de compte va donc consister crer une classe qui implmente l'interface Converter. Changeons notre bean :
... public class AccountDatas { // N'oubliez pas le get et le set private String number = "FRA 00005 123456"; ...
Vous remarquerez l'usage du tag converter qui permet d'utiliser un converter custom grce l'attribut converterId. Nous devons dclarer le converter dans le fichier faces-config.xml :
<?xml version="1.0"?> <!-Copyright 2003 Sun Microsystems, Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. --> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> ... <converter> <converter-id>com.facestut.AccountNumber</converter-id> <converter-class>com.facestut.converter.AccountNumberConverter</converter-class> </converter> ...
- 12 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
package com.facestut.converter; import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.convert.Converter; javax.faces.convert.ConverterException;
public class AccountNumberConverter implements Converter { public AccountNumberConverter() { } public String getAsString(FacesContext facesContext, UIComponent uIComponent, Object obj) { return obj.toString(); } public Object getAsObject(FacesContext facesContext, UIComponent uIComponent, String str) { if(str.length() == 16){ String regex = "[A-Z]{3} [0-9]{5} [0-9]{6}"; if(str.matches(regex)){ return str; } else { FacesMessage facesMessage = new FacesMessage(); facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR); facesMessage.setSummary("Format incorrect"); facesMessage.setDetail("Format requis : [A-Z]{3} [0-9]{5} [0-9]{6}"); throw new ConverterException(facesMessage); } } else { FacesMessage facesMessage = new FacesMessage(); facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR); facesMessage.setSummary("Format incorrect"); facesMessage.setDetail("16 caractres attendus"); throw new ConverterException(facesMessage); } }
Notez bien que dans un contexte internationalis, les messages ne doivent pas tre en dur dans le code mais dans un bundle prvu pour chaque langue.
...
<BR/> <BR/> <html:commandButton value="Valider" action="#{accountDatas.validate}"/> <html:commandButton value="Annuler" action="Index" immediate="true"/> <BR/> </html:form>
</core:view>
L'attribut immediate modifie quelque peu le cycle de traitement de la requte. JSF va excuter la phase RenderResponse puis sauter la phase RenderView. Ainsi, la phase ProcessValidation n'est pas excute et donc aucune erreur de saisie ne peut bloquer l'utilisateur. Remarquez galement que la phase UpdateModel n'est pas excute : le modle n'est pas modifi. Modifier le fichier faces-config.xml pour ajouter la rgle de navigation suivante :
<navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>Index</from-outcome> <to-view-id>/index.jsp</to-view-id> </navigation-case> </navigation-rule>
V - Les validators
Les validators concernent les composants qui permettent la saisie de valeurs. Ces composants implmentent les interfaces ValueHolder et EditableValueHolder. Par exemple, UIInput est le composant anctre de tous les composants de saisie.
V-A - Principes
La validation se dclenche lors de la phase ProcessValidation. Tout d'abord JSF rcupre la valeur soumise par le client et la stocke dans l'atrribut submittedValue (interface EditableValueHolder). Ensuite, cette valeur qui est une chane de caractres (HTTP ne connat que ce type de donnes) est convertie dans le type adquat. (voir chapitre 4). La validation peut commencer. Le but d'un validator est de protger le modle. En effet, la phase UpdateModel n'est atteinte que si la donne est valide, ce qui vite de positionner le modle dans un tat incohrent.
- 14 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Ensuite, les trois converters sont manipulables par le biais de trois tags : validateLength, validateLongRange. validateDoubleRange. Reprenons notre exemple de formulaire de saisie (chapitre 5 et 6 de Introduction JSF):
<%@ page contentType="text/html" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="html" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="core" %> <core:view> <html:form> <BR/> <html:outputText value="Nom : #{accountDatas.customer.name}"/> <BR/> <html:outputText value="Numro de compte :"/> <html:inputText id="number" value="#{accountDatas.number}"> <core:validateLength minimum="16" maximum="16"/> </html:inputText> <html:message for="number" style="color:red;" showDetail="true"/> <BR/> <html:outputText value="Date de MAJ : "/> <html:inputText value="#{accountDatas.lastModified}"/> <BR/> <html:outputText value="Total : "/> <html:outputText value="#{accountDatas.total}"/> <BR/> <html:outputText value="Montant :"/> <html:inputText id="amount" value="#{accountDatas.amount}"> <core:validateDoubleRange minimum="0" maximum="9999"/> </html:inputText> <html:message for="amount" style="color:red;"/> ... </core:view>
Lorsque la valeur saisie est invalide , on observe le mme comportement que pour les converters.
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT ...
Vous remarquerez l'usage du tag validator qui permet d'utiliser un validator custom grce l'attribut validatorId. Nous devons dclarer le validator dans le fichier faces-config.xml :
<?xml version="1.0"?> <!-Copyright 2003 Sun Microsystems, Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. --> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> ... <validator> <validator-id>com.facestut.AccountNumber</validator-id> <validator-class>com.facestut.validator.AccountNumberValidator</validator-class> </validator>
public class AccountNumberValidator implements Validator { public AccountNumberValidator(){ } public void validate(FacesContext facesContext,UIComponent component,Object value){ String accountNumber = (String)value; String[] parts = accountNumber.split(" "); String customerNumberAsString = parts[2]; long customerNumber = Long.valueOf(customerNumberAsString).longValue(); boolean even = ((customerNumber / 2)*2) == customerNumber; if(even){ FacesMessage facesMessage = new FacesMessage(); facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR); facesMessage.setSummary("Le numro client est pair ! (impair attendu)"); throw new ValidatorException(facesMessage); }
- 16 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
VI - Dvelopper un composant JSF VI-A - Principes Un composant JSF consiste en trois lments dont un est optionnel :
la classe du tag : dfinit une classe qui va faire le lien entre la page JSP et le composant. la classe du composant : implmente le composant aux travers ses attributs et mthodes. la classe du renderer : optionnelle, cette classe est spcialise dans le rendu du composant (HTML,XML,XUL, ...)
JSF tablit des conventions de nommage pour ces classes. Ainsi, la classe du tag aura pour nom <nomducomposant>Tag, la classe du composant UI<nomducomposant> et la classe du renderer <nomducomposant>Renderer.
un contrle page suivante un contrle page prcdente le nombre d'lments dans la liste paramtrable (nombre d'lments par page) composant fils d'un UIData via le tag dataTable et le tag facet (header ou footer)
public class UIPager extends UIComponentBase { public String getFamily(){ return "facestut"; } public void encodeBegin(FacesContext facesContext) throws IOException { ResponseWriter responseWriter = facesContext.getResponseWriter(); // Encode le contrle page prcdente responseWriter.startElement("a", this); responseWriter.writeAttribute("href","#","href"); responseWriter.write("<<"); responseWriter.endElement("a");
- 17 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT public void encodeEnd(FacesContext facesContext) throws IOException { ResponseWriter responseWriter = facesContext.getResponseWriter(); // Encode le contrle page suivante responseWriter.startElement("a", this); responseWriter.writeAttribute("href","#","href"); responseWriter.write(">>"); responseWriter.endElement("a"); // Encode les informations de taille UIData uiData = getUIData(); responseWriter.startElement("p", this); responseWriter.write(uiData.getRowCount() + " lments." ); responseWriter.endElement("p");
public boolean getRendersChildren(){ return true; } protected UIData getUIData(){ return (UIData) this.getParent(); }
Notre composant hrite de UIComponentBase. Cette classe est la classe de base pour crer un composant JSF. Notez que UIComponentBase a des classes filles qui modlise des types de composants plus spcifiques : UIPanel, UICommand, ... Hriter de UIComponentBase implique l'implmentation de la mthode getFamily(). Cette mthode renvoie la famille de votre composant. Une famille permet de regrouper des composants. Cette notion est utilise en conjonction avec le concept de Renderer que nous verrons plus tard.
La classe UIComponentBase implmente diffrentes mthodes qui sont directement lies au cycle de traitement d'une requte JSF (voir chapitre 2). Dans notre cas, nous ne modifierons pas le comportement de ces mthodes. Les mthodes d'encodage vont donc se charger du rendu de notre composant. La prsence de trois mthodes encode est lie l'utilisation de langage balises pour gnrer le rendu. En effet, tous les langages balises sont issus de XML. Une balise XML un dbut une fin et ventuellement contient des balises filles. Nous avons la correspondance suivante :
<xxx>: encodeBegin encodeChildren : <yyy></yyyy> <yyy></yyyy> ..... </xxx>: encodeEnd
Notez bien que ce dcoupage est thorique. Dans certains cas un composant ne rend pas ces fils, la mthode encodeChildren n'est pas implmente et getRenderChildren retourne false.
- 18 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Dans d'autres cas, la notation abrge d'une balise est utilise car le composant ne peut avoir de fils, encodeBegin n'est pas implmente et tout le code de rendu est dans encodeEnd (<xxx />). Notre composant n'est pas termin mais nous pouvons tout de mme tester son rendu grce au tag pager. Avant cela nous devons indiquer JSF qu'un nouveau composant est disponible. Le fichier de configuration facesconfig.xml va nous permettre cela :
<?xml version="1.0"?> <!-Copyright 2003 Sun Microsystems, Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. --> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> ... <managed-bean> <managed-bean-name>accountDatas</managed-bean-name> <managed-bean-class>com.facestut.bean.AccountDatas</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> ... <converter> <converter-id>com.facestut.AccountNumber</converter-id> <converter-class>com.facestut.converter.AccountNumberConverter</converter-class> </converter> ... <navigation-rule> <!-- Indique pour quelle vue courante la rgle s'applique --> <from-view-id>/index.jsp</from-view-id> ... </navigation-rule> <component> <component-type>facestut.component.Pager</component-type> <component-class>com.facestut.component.UIPager</component-class> </component> </faces-config>
- 19 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT public String getComponentType(){ return "facestut.component.Pager"; }
Cette classe tends UIComponentTag qui est la classe anctre de tout tag JSF. Un tag JSF n'a d'autre but que de crer une instance de composant. La mthode getComponentType() renvoie le type du composant crer tandis que la mthode getRendererType() renvoie le nom du renderer qui effectue le rendu du composant Lorsque cette mthode renvoie null le composant effectue lui mme son rendu dans les mthodes encodeXXX. Nous devons maintenant crer le fichier /Web Pages/META-INF/facestut.tld afin de dclarer notre tag :
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>ft</short-name> <uri>http://facestut.developpez.com/jsf</uri> <tag> <name>pager</name> <tag-class>com.facestut.tag.PagerTag</tag-class> <body-content>empty</body-content> </tag> </taglib>
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT <html:outputText value="#{customer.forname}"/> </html:column> </html:dataTable> <br> <html:commandButton value="Supprimer les clients" action="#{bankCtrl.removeSelectedCustomers}"/> <html:commandButton value="Ajouter un client" action="#{bankCtrl.addCustomer}"/> </html:form> </core:view>
public class UIPager extends UIComponentBase { private Integer itemsByPage; .... public Integer getItemsByPage(){ return this.itemsByPage; } public void setItemsByPage(Integer integer){ this.itemsByPage = integer; } public Object saveState(FacesContext context) { Object values[] = new Object[2]; values[0] = super.saveState(context); values[1] = this.itemsByPage; return values; }
- 21 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT public void restoreState(FacesContext context, Object state) { Object values[] = (Object[]) state; super.restoreState(context, values[0]); this.itemsByPage = (Integer)values[1]; }
Le cycle de vie JSF prvoit une phase de sauvegarde et une phase de restauration de l'tat de tout composant JSF. Ces phases appellent les mthodes saveState et restoreState sur chaque composant de la vue JSF courante (ou UIViewRoot). Voir le chapitre 2 ce sujet. Ensuite nous devons modifier la classe du tag :
package com.facestut.tag; import javax.faces.component.UIComponent; import javax.faces.webapp.UIComponentTag; public class PagerTag extends UIComponentTag { private Integer itemsByPage; public String getRendererType(){ return null; } public String getComponentType(){ return "facestut.component.Pager"; } public Integer getItemsByPage(){ return this.itemsByPage; } public void setItemsByPage(Integer integer){ this.itemsByPage = integer; } public void setProperties(UIComponent component){ super.setProperties(component); if(getItemsByPage() != null){ component.getAttributes().put("itemsByPage",getItemsByPage()); } }
La mthode setProperties(UIComponent) est une mthode de UIComponentTag. Elle a pour responsabilit de mettre jour les attributs du composant nouvellement cre. A cet effet, la classe UIComponent offre un mcanisme intressant via une Map d'attributs.
Lorsque vous invoquez la mthode put(String,Object) sur celle-ci, deux comportements sont possibles:
Soit la classe du composant possde un attribut itemsByPage avec ces getter et setter : la mthode put mets jour cet attribut via le setter Soit l'attribut n'existe pas dans la classe et la Map se comporte comme une Map normale.
Remarque : on observe le mme algorithme pour la lecture de l'attribut mais la mthode get(String).
- 22 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Nous pouvons dsormais utiliser cet attribut dans nos pages. Cependant, l'attribut itemsByPage est mis jour mais nous n'avons pas effectu de traitement spcifique pour indiquer au composant UIData de ne rendre qu'une partie de la liste d'objets.
package com.facestut.tag; import import import import import com.facestut.component.UIPager; javax.faces.component.UIComponent; javax.faces.component.UIData; javax.faces.webapp.UIComponentTag; javax.servlet.jsp.JspException;
public class PagerTag extends UIComponentTag { ... public int doEndTag() throws JspException { if(getCreated()){ UIPager uiPager = (UIPager)getComponentInstance(); UIComponent uiComponent = uiPager.getParent(); if(uiComponent instanceof UIData){ UIData uiData = (UIData)uiComponent; if(getItemsByPage() != null){ int rows = getItemsByPage().intValue(); uiData.setRows(rows); } } else { throw new JspException("Le parent du pager n'est pas un htmlDataTable !"); } } return super.doEndTag(); }
La mthode doEndTag() est appele lorsque la balise de fin de tag est rencontre. Il est souvent utile de redfinir cette mthode pour effectuer des traitements. Ici, nous utilisons la mthode getCreated() pour savoir si le composant UIPager vient d'tre crer ou si il existe dj.
- 23 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
En effet, lorsque vous rechargez une page JSF, la mthode setProperties(UIComponent) du tag n'est plus invoque moins que la page JSP ait chang. Cette mthode est invoque lorsque le composant est cr pour la premire fois. La mthode getCreated() nous donne cette information. Ici, nous allons contrler que le composant parent est bien un UIData et changer l'attribut rows afin de limiter le nombre d'objets visibles. Notez bien que UIData ne gre pas la notion de page. Remarque importante : lorsque le composant est cre et initialis dans setProperties(UIComponent), il n'est pas encore insr dans l'arbre de composants, ce qui veut dire que sa mthode getParent() renvoie null.
- 24 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT public void encodeChildren(FacesContext facesContext) throws IOException { } public void encodeEnd(FacesContext facesContext) throws IOException { ResponseWriter responseWriter = facesContext.getResponseWriter(); String clientId = getClientId(facesContext); // Encode le contrle page suivante responseWriter.startElement("a", this); responseWriter.writeAttribute("href","javascript:document.forms[0].submit(); ","href"); responseWriter.writeAttribute("onclick","document.getElementById('" + clientId +"').value = 'next';return true;","onclick"); responseWriter.write(">>"); responseWriter.endElement("a"); // Encode les informations de taille UIData uiData = getUIData(); responseWriter.startElement("p", this); responseWriter.write(uiData.getRowCount() + " lments." ); responseWriter.endElement("p"); responseWriter.startElement("input", this); responseWriter.writeAttribute("id",clientId,"id"); responseWriter.writeAttribute("name",clientId,"name"); responseWriter.writeAttribute("type","hidden","type"); responseWriter.writeAttribute("value","","value"); } ...
Nous utilisons ici un champs cach dont le nom est le clientId du composant UIPager courant. En effet, le client id a pour rle d'identifier de manire unique une instance de composant qui est rendu ct client. Lorsque l'utilisateur clique sur suivant ou prcdent un bout de code Javascript mets jour le champs qui reprsente le composant et son tat. Le formulaire est ensuite soumis. Nous pouvons maintenant dcoder les paramtres de la requte HTTP afin d'y retrouver notre champs cach et sa valeur :
public void decode(FacesContext facesContext){ String clientId = getClientId(facesContext); Map parametersMap = facesContext.getExternalContext().getRequestParameterMap(); Object value = parametersMap.get(clientId); if(value != null){ String cmd = (String)value; UIData uiData = (UIData) getParent(); if(itemsByPage!= null){ if(cmd.equals("prev")){ int computedFirst = uiData.getFirst()-itemsByPage.intValue(); if(computedFirst < 0){ computedFirst = 0; } uiData.setFirst(computedFirst); } else if(cmd.equals("next")){ int computedFirst = uiData.getFirst()+itemsByPage.intValue(); if(computedFirst >= uiData.getRowCount()){ computedFirst = uiData.getFirst(); } uiData.setFirst(computedFirst); - 25 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
Le composant UIPager n'est pas trs user friendly: il pourrait afficher par exemple la page courante ou une liste droulante contenant les numros de page. A vous de jouer !
VI-G - Le renderer
Un renderer est une classe spcialise dans l'encodage et le dcodage d'un type de composant particulier. En effet, le composant JSF dfinit le composant en dehors de toute reprsentation. Cependant, la prsence des mthodes d'encodage et de dcodage brise cette indpendance. Dans le cas de UIPager ces mthodes contiennent du code HTML et un algorithme de dcodage particulier. Il est possible de dlguer ces responsabilits un renderer. Un renderer est une classe Java qui tends la classe javax.faces.Renderer. Voici la classe PagerRenderer :
package com.facestut.renderer; import import import import import import import import com.facestut.component.UIPager; java.io.IOException; java.util.Map; javax.faces.component.UIComponent; javax.faces.component.UIData; javax.faces.context.FacesContext; javax.faces.context.ResponseWriter; javax.faces.render.Renderer;
public class PagerRenderer extends Renderer { public void encodeBegin(FacesContext facesContext,UIComponent component) throws IOException { ResponseWriter responseWriter = facesContext.getResponseWriter(); String clientId = component.getClientId(facesContext); responseWriter.startElement("a", component); responseWriter.writeAttribute("href","javascript:document.forms[0].submit(); ","href"); responseWriter.writeAttribute("onclick","document.getElementById('" + clientId +"').value = 'prev';return true;","onclick"); responseWriter.write("<<"); responseWriter.endElement("a"); } public void encodeEnd(FacesContext facesContext,UIComponent component) throws IOException { ResponseWriter responseWriter = facesContext.getResponseWriter(); - 26 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT String clientId = component.getClientId(facesContext); responseWriter.startElement("a", component); responseWriter.writeAttribute("href","javascript:document.forms[0].submit(); ","href"); responseWriter.writeAttribute("onclick","document.getElementById('" + clientId +"').value = 'next';return true;","onclick"); responseWriter.write(">>"); responseWriter.endElement("a"); UIPager uiPager = (UIPager)component; UIData uiData = uiPager.getUIData(); responseWriter.startElement("p", component); responseWriter.write(uiData.getRowCount() + " lments." ); responseWriter.endElement("p"); responseWriter.startElement("input", component); responseWriter.writeAttribute("id",clientId,"id"); responseWriter.writeAttribute("name",clientId,"name"); responseWriter.writeAttribute("type","hidden","type"); responseWriter.writeAttribute("value","","value");
public void decode(FacesContext facesContext,UIComponent component){ String clientId = component.getClientId(facesContext); Map parametersMap = facesContext.getExternalContext().getRequestParameterMap(); Object value = parametersMap.get(clientId); if(value != null){ String cmd = (String)value; UIData uiData = (UIData) component.getParent(); Integer itemsByPage = (Integer)component.getAttributes().get("itemsByPage"); if(itemsByPage!= null){ if(cmd.equals("prev")){ int computedFirst = uiData.getFirst()-itemsByPage.intValue(); if(computedFirst < 0){ computedFirst = 0; } uiData.setFirst(computedFirst); } else if(cmd.equals("next")){ int computedFirst = uiData.getFirst()+itemsByPage.intValue(); if(computedFirst >= uiData.getRowCount()){ computedFirst = uiData.getFirst(); } uiData.setFirst(computedFirst); } } } }
L'intrt du renderer est de pouvoir dlguer des responsabilits du composant une classe spcialise pour une configuration particulire. En effet, le renderer d'un composant est valable dans une configuration donne ce qui permet de changer de renderer en fonction de la technologie de rendu utilise (WAP, HTML, XUL, ...). L'association d'un renderer est d'un composant se fait au niveau du tag. La mthode getRendererType() indique le type de renderer utilis :
... public class PagerTag extends UIComponentTag { private Integer itemsByPage; public String getRendererType(){ return "facestut.renderer.Pager"; - 27 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/
JSF, quelques concepts avancs avec NetBeans par Olivier SCHMITT } public String getComponentType(){ return "facestut.component.Pager"; } ...
La balise render-kit dfinit un kit de rendu. Un kit de rendu est un ensemble de renderer. Ainsi vous pouvez changer le rendu d'un composant en changeant son renderer. Ici, il suffit de changer la classe com.facestut.renderer.PagerRenderer en un autre nom de classe.
VII - Conclusion
Nous avons vu dans ce tutoriel comment aborder le dveloppement d'une application JSF avec des concepts nouveaux, pour un rsultat plus professionnel : la validation et la conversion de la saisie utilisateur les messages les composants customs
Cependant, JSF propose des fonctionnalits encore plus avances qui permettent de customiser son fonctionnement mme, comme par exemple : Utiliser XML pour construire les vues Utiliser un gestionnaire de navigation custom pour par exemple produire des applications type workflow ...
Et JSF 1.2 qui sortira au printemps 2006 apporte son lot de nouveauts ...
- 28 Copyright 2006 Olivier Schmitt. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://schmitt.developpez.com/tutoriel/java/jsf/advanced/