IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]


 

76. JSF (Java Server Faces)

 

chapitre 7 6

 

Niveau : niveau 4 Supérieur 

 

76.1. La présentation de JSF

Les technologies permettant de développer des applications web avec Java ne cessent d'évoluer : 

  1. Servlets
  2. JSP
  3. MVC Model 1 : servlets + JSP
  4. MVC Model 2 : un seule servlet + JSP
  5. Java Server Faces

Java Server Faces (JSF) est une technologie dont le but est de proposer un framework qui facilite et standardise le développement d'applications web avec Java. Son développement a tenu compte des différentes expériences acquises lors de l'utilisation des technologies standard pour le développement d'applications web (servlet, JSP, JSTL) et de différents frameworks (Struts, ...).

Le grand intérêt de JSF est de proposer un framework qui puisse être mis en oeuvre par des outils pour permettre un développement de type RAD pour les applications web et ainsi faciliter le développement des applications de ce type. Ce type de développement était déjà courant pour des applications standalone ou clients/serveurs lourds avec des outils tels que Delphi de Borland, Visual Basic de Microsoft ou Swing avec Java.

Ce concept n'est pourtant pas nouveau dans les applications web puisqu'il est déjà mis en oeuvre par WebObject d'Apple et plus récemment par ASP.Net de Microsoft mais cette mise en oeuvre à grande échelle fût relativement tardive. L'adoption du RAD pour le développement web trouve notamment sa justification dans le coût élevé de développement de l'IHM à la « main » et souvent par copier/coller d'un mélange de plusieurs technologies (HTML, JavaScript, ...), rendant fastidieux et peu fiable le développement de ces applications.

Plusieurs outils commerciaux intègrent déjà l'utilisation de JSF notamment Studio Creator de Sun, WSAD d'IBM, JBuilder de Borland, JDevelopper d'Oracle, ...

Même si JSF peut être utilisé par codage à la main, l'utilisation d'un outil est fortement recommandée pour pouvoir mettre en oeuvre rapidement toute la puissance de JSF.

Ainsi de par sa complexité et sa puissance, JSF s'adapte parfaitement au développement d'applications web complexes en facilitant leur écriture.

Les pages officielles de cette technologie sont à l'URL :
http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.phpl.

La version 1.0 de Java Server Faces, développée sous la JSR-127, a été validée en mars 2004.

JSF est une technologie utilisée côté serveur dont le but est de faciliter le développement de l'interface utilisateur en séparant clairement la partie « interface » de la partie  « métier » d'autant que la partie interface n'est souvent pas la plus compliquée mais la plus fastidieuse à réaliser.

Cette séparation avait déjà été initiée avec la technologie JSP et particulièrement les bibliothèques de tags personnalisés. Mais JSF va encore plus loin en reposant sur le modèle MVC et en proposant de mettre en oeuvre :

  • l'assemblage de composants serveurs qui génèrent le code de leur rendu avec la possibilité d'associer certains composants à une source de données encapsulée dans un bean
  • l'utilisation d'un modèle de développement standardisé reposant sur l'utilisation d'événements et de listeners
  • la conversion et la validation des données avant leur utilisation dans les traitements
  • la gestion de l'état des composants de l'interface graphique
  • la possibilité d'étendre les différents modèles et de créer ses propres composants
  • la configuration de la navigation entre les pages
  • le support de l'internationalisation
  • le support pour l'utilisation par des outils graphiques du framework afin de faciliter sa mise en oeuvre

JSF se compose :

  • d'une spécification qui définit le mode de fonctionnement du framework et une API : l'ensemble des classes de l'API est contenu dans les packages javax.faces.
  • d'une implémentation de référence
  • de bibliothèques de tags personnalisés fournies par l'implémentation pour utiliser les composants dans les JSP, gérer les événements, valider les données saisies, ...

Le rendu des composants ne se limite pas à une seule technologie même si l'implémentation de référence ne propose qu'un rendu des composants en HTML.

Le traitement d'une requête gérée par une application utilisant JSF suit un cycle de vie particulier constitué de plusieurs étapes :

  • création de l'arbre de composants
  • extraction des données des différents composants de la page
  • conversion et validation des données
  • extraction des données validées et mise à jour du modèle de données (javabean)
  • traitements des événements liés à la page
  • génération du rendu de la réponse

Ces différentes étapes sont transparentes lors d'une utilisation standard de JSF.

Ce chapitre contient plusieurs sections :

 

76.2. Le cycle de vie d'une requête

JSF utilise la notion de vue (view) qui est composée d'une arborescence ordonnée de composants inclus dans la page.

Les requêtes sont prises en charge et gérées par le contrôleur d'une application JSF (en général une servlet). Celle-ci va assurer la mise en oeuvre d'un cycle de vie des traitements en vue d'envoyer une réponse au client.

JSF propose pour chaque page un cycle de vie pour traiter la requête HTTP et générer la réponse. Ce cycle de vie est composé de plusieurs étapes :

  1. Restore view ou Reconstruct Component Tree : cette première phase permet au serveur de recréer l'arborescence des composants qui composent la page. Cette arborescence est stockée dans un objet de type FacesContext et sera utilisée tout au long du traitement de la requête.
  2. Apply Request Value : dans cette étape, les valeurs des données sont extraites de la requête HTTP pour chaque composant et sont stockées dans leur composant respectif dans le FaceContext. Durant cette phase des opérations de conversions sont réalisées pour permettre de transformer les valeurs stockées sous forme de chaînes de caractères dans la requête http en un type utilisé pour le stockage des données.
  3. Perform validations : une fois les données extraites et converties, il est possible de procéder à leur validation en appliquant les validators enregistrés auprès de chaque composant. Les éventuelles erreurs de conversions sont stockées dans le FaceContext. Dans ce cas, l'étape suivante est directement « Render Response » pour permettre de réafficher la page avec les valeurs saisies et afficher les erreurs
  4. Synchronize Model  ou update model values : cette étape permet de stocker dans les composants du FaceContext leurs valeurs locales validées respectives. Les éventuelles erreurs de conversions sont stockées dans le FaceContext. Dans ce cas, l'étape suivante est directement « Render Response » pour permettre de réafficher la page avec les valeurs saisies et d' afficher les erreurs
  5. Invoke Application Logic : dans cette étape, le ou les événements émis dans la page sont traités. Cette phase doit permettre de déterminer la page résultat qui sera renvoyée dans la réponse en utilisant les règles de navigation définies dans l'application. L'arborescence des composants de cette page est créée.
  6. Render Response : cette étape se charge de créer le rendu de la page de la réponse.

 

76.3. Les implémentations

Java Server Faces est une spécification : il est donc nécessaire d'obtenir une implémentation de la part d'un tiers.

Plusieurs implémentations commerciales ou libres sont disponibles, notamment l'implémentation de référence de Sun et MyFaces qui est devenu un projet du groupe Apache.

 

76.3.1. L'implémentation de référence

Comme pour toute JSR validée, Sun propose une implémentation de référence des spécifications de la JSR, qui est la plus complète possible.

Plusieurs versions de l'implémentation de référence de Sun sont proposées :

Version Date de diffusion
1.0 Mars 2004
1.1 Mai 2004
1.1_01 Septembre 2004

La solution la plus simple pour utiliser l'implémentation de référence est d'installer le JWSDK 1.3 qui est fourni en standard avec l'implémentation de référence de JSF. La version de JSF fournie avec le JWSDK 1.3 est la 1.0.

Pour utiliser la version 1.1, il faut supprimer le répertoire jsf dans le répertoire d'installation de JWSDK, télécharger l'implémentation de référence, décompresser son contenu dans le répertoire d'installation de JWSDK et renommer le répertoire jsf-1_1_01 en jsf.

Il est aussi possible de télécharger l'implémentation de référence sur le site de Sun et de l'installer « manuellement » dans un conteneur web tel que Tomcat. Cette procédure sera détaillée dans une des sections suivantes.

Pour cela, il faut télécharger le fichier jsf-1_1_01.zip et le décompresser dans un répertoire du système. L'archive contient les bibliothèques de l'implémentation, la documentation des API et des exemples.

Les exemples de ce chapitre vont utiliser cette version 1.1 de l'implémentation de référence des JSF.

 

76.3.2. MyFaces

MyFaces

MyFaces est une implémentation libre des Java Server Faces qui est devenue un projet du groupe Apache.

Elle propose plusieurs composants spécifiques en plus de ceux imposés par les spécifications JSF.


Le site de MyFaces est à l'URL : http://myfaces.apache.org/

Il faut télécharger le fichier et le décompresser dans un répertoire du système. Il suffit alors de copier le fichier myfaces-examples.war dans le répertoire webapps de Tomcat, de relancer le serveur puis saisir l'URL http://localhost:8080/myfaces-examples

Pour utiliser MyFaces dans ses propres applications, il faut réaliser plusieurs opérations.

Il faut copier les fichiers *.jar du répertoire lib de MyFaces et myfaces-jsf-api.jar dans le répertoire WEB-INF/lib de la webapp.

Dans chaque page qui va utiliser les composants de MyFaces, il faut déclarer la bibliothèque de tags dédiés.

Exemple :
<%@ taglib uri="http://myfaces.sourceforge.net/tld/myfaces_ext_0_9.tld" prefix="x"%>

 

76.4. Le contenu d'une application

Les applications utilisant JSF sont des applications web qui doivent respecter les spécifications de J2EE.

En tant que telles, elles doivent avoir la structure définie par J2EE pour toutes les applications web :

/
/WEB-INF
/WEB-INF/web.xml
/WEB-INF/lib
/WEB-INF/classes

Le fichier web.xml doit contenir au minimum certaines informations notamment, la servlet faisant office de contrôleur, le mapping des URL pour cette servlet et des paramètres.

Exemple :
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>Test JSF</display-name>
  <description>Application de tests avec JSF</description>
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>
  <!-- Faces Servlet -->
  <servlet>
    <servlet-name>Faces Servlet</servlet-name> 
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>
  </servlet>
  <!-- Faces Servlet Mapping -->
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
</web-app>

Chaque implémentation nécessite un certain nombre de bibliothèques tierces pour son bon fonctionnement.

Par exemple, pour l'implémentation de référence, les bibliothèques suivantes sont nécessaires :

jsf-api.jar
jsf-ri.jar
jstl.jar
standard.jar
common-beanutils.jar
commons-digester.jar
commons-collections.jar
commons-logging.jar

Remarque : avec l'implémentation de référence, il n'y a aucun fichier .tld à copier car ils sont intégrés dans le fichier jsf-impl.jar.

Les fichiers nécessaires dépendent de l'implémentation utilisée.

Ces bibliothèques peuvent être mises à disposition de l'application selon plusieurs modes :

  • incorporées dans le package de l'application dans le répertoire /WEB-INF/lib
  • incluses dans le répertoire des bibliothèques partagées par les applications web des conteneurs web s'ils proposent une telle fonctionnalité. Par exemple avec Tomcat, il est possible de copier ces bibliothèques dans le répertoire shared/lib.

L'avantage de la première solution est de faciliter la portabilité de l'application sur différents conteneur web mais elle duplique ces fichiers si plusieurs applications utilisent JSF.

Les avantages et inconvénients de la première solution sont exactement à l'opposé de ceux de la seconde solution. Le choix de l'une ou l'autre est donc à faire en fonction du contexte de déploiement.

 

76.5. La configuration de l'application

Toute application utilisant JSF doit posséder au moins deux fichiers de configuration qui vont contenir les informations nécessaires à sa bonne exécution.

Le premier fichier est le descripteur de toute application web J2EE : le fichier web.xml contenu dans le répertoire WEB-INF.

Le second fichier est un fichier de configuration au format XML, particulier au paramétrage de JSF et nommé faces-config.xml.

 

76.5.1. Le fichier web.xml

Le fichier web.xml doit contenir au minimum certaines informations notamment, la servlet servant de contrôleur, le mapping des URLs pour cette servlet et des paramètres pour configurer JSF.

Exemple :
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>Test JSF</display-name>
  <description>Application de tests avec JSF</description>
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>

  <!-- Servlet servant de controleur-->
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>  
    <load-on-startup> 1 </load-on-startup>
  </servlet>

  <!-Le mapping de la servlet -->
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
</web-app>

Le tag <servlet> permet de définir une servlet et plus particulièrement dans ce cas de préciser la servlet qui sera utilisée comme contrôleur dans l'application. Le plus simple est d'utiliser la servlet fournie avec l'implémentation de référence javax.faces.webapp.FacesServlet. Le tag <load-on-startup> avec comme valeur 1 permet de demander le chargement de cette servlet au lancement de l'application.

Le tag <servlet-mapping> permet de préciser le mapping des URLs qui seront traitées par la servlet. Ce mapping peut prendre deux formes :

  • mapping par rapport à une extension : exemple <url-pattern>*.faces</url-pattern>.
  • mapping par rapport à un préfixe : exemple <url-pattern>/faces/*</url-pattern>.

Les URL utilisées pour des pages mettant en oeuvre JSF doivent obligatoirement passer par cette servlet. Ces URLs peuvent être de deux formes selon le mapping défini.

Exemple :

  • http://localhost:8080/nom_webapp/index.faces
  • http://localhost:8080/nom_webapp/faces/index.jsp

Dans les deux cas, c'est la servlet utilisée comme contrôleur qui va déterminer le nom de la page JSP à utiliser.

Le paramètre de contexte javax.faces.STATE_SAVING_METHOD permet de préciser le mode d'échange de l'état de l'arbre des composants de la page. Deux valeurs sont possibles :

  • client :
  • server :

Il est possible d'utiliser l'extension .jsf pour les fichiers JSP utilisant JSF à condition de correctement configurer le fichier web.xml dans ce sens. Pour cela deux choses sont à faire :

  • il faut demander le mapping des URL terminant par .jsf par la servlet

    <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>


  • il faut préciser à la servlet le suffix par défaut à utiliser

    <context-param>
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    <param-value>.jsf</param-value>
    </context-param>

Le démarrage d'une application directement avec une page par défaut utilisant JSF ne fonctionne pas correctement. Il est préférable d'utiliser une page HTML qui va effectuer une redirection vers la page d'accueil de l'application

Exemple :
<html>
  <head>
    <meta http-equiv="Refresh" content= "0; URL=index.faces"/>
    <title>Demarrage de l'application</title>
  </head>
  <body>
    <p>D&eacute;marrage de l'application ...</p>
  </body>
</html>

Il suffit alors de préciser dans le fichier web.xml que cette page est la page par défaut de l'application.

Exemple :
...
<welcome-file-list>
  <welcome-file>index.php</welcome-file>
</welcome-file-list>
...

 

76.5.2. Le fichier faces-config.xml

Le plus simple est de placer ce fichier dans le répertoire WEB-INF de l'application Web.

Il est aussi possible de préciser son emplacement dans un paramètre de contexte nommé javax.faces.application.CONFIG_FILES dans le fichier web.xml. Il est possible par ce biais de découper le fichier de configuration en plusieurs morceaux. Ceci est particulièrement intéressant pour de grosses applications car un seul fichier de configuration peut dans ce cas devenir très gros. Il suffit de préciser chacun des fichiers séparés par une virgule dans le tag <param-value>.

Exemple :
...
<context-param>
  <param-name>javax.faces.application.CONFIG_FILES</param-name>
  <param-value>
/WEB-INF/ma-faces-config.xml, /WEB-INF/navigation-faces.xml, /WEB-INF/beans-faces.xml
  </param-value>
</context-param>
...

Ce fichier au format XML permet de définir et de fournir des valeurs d'initialisation pour des ressources nécessaires à l'application utilisant JSF.

Ce fichier doit impérativement respecter la DTD proposée par les spécifications de JSF :

http://java.sun.com/dtd/web-facesconfig_1_0.dtd

Le tag racine du document XML est le tag <face-config>. Ce tag peut avoir plusieurs tags fils :

Tag Rôle
application permet de préciser ou de remplacer des éléments de l'application
factory permet de remplacer des fabriques par des fabriques personnalisées de certaines ressources (FacesContextFactory, LifeCycleFactory, RenderKitFactory, ...)
component définit un composant graphique personnalisé
converter définit un convertisseur pour encoder/décoder les valeurs des composants graphiques (conversion de String en Object et vice versa)
managed-bean définit un objet utilisé par un composant qui est automatiquement créé, initialisé et stocké dans une portée précisée
navigation-rule définit les règles qui permettent de déterminer l'enchaînement des traitements de l'application
referenced-bean  
render-kit définit un kit pour le rendu des composants graphiques
lifecycle  
validator définit un validateur personnalisé de données saisies dans un composant graphique

Ces tags fils peuvent être utilisés 0 ou plusieurs fois dans le tag <face-config>.

Le tag <application> permet de préciser des informations sur les entités utilisées par l'internationalisation et/ou de remplacer des éléments de l'application.

Les éléments à remplacer peuvent être : ActionListener, NavigationHandler, ViewHandler, PropertyResolver, VariableResolver. Ceci n'est utile que si la version fournie dans l'implémentation ne correspond pas aux besoins et doit être personnalisée par l'écriture d'une classe dédiée.

Le tag fils <message-bundle> permet de préciser le nom de base des fichiers de ressources utiles à l'internationalisation.

Le tag <locale-config> permet de préciser les locales qui sont supportées par l'application. Il faut utiliser autant de tags fils <supported-locale> que de locales supportées. Le tag fil <default-locale> permet de préciser la locale par défaut.

Exemple :
...
<application> 
  <message-bundle>com.jmdoudoux.test.jsf.monapp.bundles.Messages</message-bundle> 
  <locale-config> 
    <default-locale>fr</default-locale> 
    <supported-locale>en</supported-locale> 
  </locale-config> 
</application> 
...

 

76.6. Les beans

Les beans sont largement utilisés dans une application JSF notamment pour permettre l'échange de données entre les différentes entités et le traitement des événements.

Les beans sont des classes qui respectent une spécification particulière notamment la présence :

  • de getters et de setters qui respectent une convention de nommage particulière pour les attributs
  • un constructeur par défaut sans arguments

 

76.6.1. Les beans managés (managed bean)

Les beans managés sont des javabeans dont le cycle de vie va être contrôlé par le framework JSF en fonction des besoins et du paramétrage fourni dans le fichier de configuration.

Dans le fichier de configuration, chacun de ces beans doit être déclaré avec un tag <managed-bean>. Ce tag possède trois tags fils obligatoires :

  • <managed-bean-name> : le nom attribué au bean (celui qui sera utilisé lors de son utilisation)
  • <managed-bean-class> : le type pleinement qualifié de la classe du bean
  • <managed-bean-scope> : précise la portée dans laquelle le bean sera stocké et donc utilisable

La portée peut prendre les valeurs suivantes :

  • request : cette portée est limitée entre l'émission de la requête et l'envoi de la réponse. Les données stockées dans cette portée sont utilisables lors d'un transfert vers une autre page (forward). Elles sont perdues lors d'une redirection (redirect).
  • session : cette portée permet la circulation de données entre plusieurs échanges avec un même client
  • application : cette portée permet l'accès à des données pour toutes les pages d'une même application quelque soit l'utilisateur
Exemple :
...
<managed-bean> 
  <managed-bean-name>login</managed-bean-name> 
  <managed-bean-class>com.jmd.test.jsf.LoginBean</managed-bean-class> 
  <managed-bean-scope>session</managed-bean-scope> 
</managed-bean> 
...

Il est possible de fournir des valeurs par défaut aux propriétés en utilisant le tag <managed-property>. Ce tag possède deux tags fils :

  • <property-name> : nom de la propriété du bean
  • <value> : valeur à associer à la propriété
Exemple :
...
<managed-bean>
  <managed-bean-name>login</managed-bean-name>
  <managed-bean-class>com.jmd.test.jsf.LoginBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
  <managed-property>
    <property-name>nom</property-name>
    <value>test</value>
  </managed-property>
</managed-bean>
...

Lorsque le bean sera instancié, JSF appellera automatiquement les setters des propriétés identifiées dans des tags <managed-property> avec leurs valeurs <value> respectives.

Pour initialiser la propriété à null, il faut utiliser le tag <null-value>

Exemple :
...
<managed-property> 
  <property-name>nom</property-name> 
  <null-value> 
</managed-property> 
...

Ces informations seront utilisées par JSF pour automatiser la création ou la récupération d'un bean lorsque celui-ci sera utilisé dans l'application.

Le grand intérêt de ce mécanisme est de ne pas avoir à se soucier de l'instanciation du bean ou de sa recherche dans la portée puisque c'est le framework qui va s'en occuper de façon transparente.

 

76.6.2. Les expressions de liaison de données d'un bean

Il est toujours nécessaire dans la partie présentation d'obtenir la valeur d'une donnée d'un bean pour par exemple l'afficher.

JSF propose une syntaxe basée sur des expressions qui facilitent l'utilisation des valeurs d'un bean. Ces expressions doivent être délimitées par #{ et }.

Basiquement une expression est composée du nom du bean suivi du nom de la propriété désirée séparés par un point.

Exemple :
<h:inputText value="#{login.nom}"/>

Cet exemple affecte la valeur de l'attribut nom du bean login au composant de type saisie de texte. Dans ce cas précis, c'est aussi cet attribut de ce bean qui recevra la valeur saisie lorsque la page sera envoyée au serveur.

En fonction du contexte le résultat de l'évaluation peut conduire à l'utilisation du getter (par exemple pour afficher la valeur) ou du setter (pour affecter la valeur après un envoi de la page). C'est JSF qui le détermine en fonction du contexte.

La notation par point peut être remplacée par l'utilisation de crochets. Dans ce cas, le nom de la propriété doit être mis entre simples ou doubles quotes dans les crochets.

Exemple :
login.nom
login["nom"]
login['nom']

Ces trois expressions sont rigoureusement identiques. Cette syntaxe peut être plus pratique lors de la manipulation de collections mais elle est obligatoire lorsque la propriété contient un point.

Exemple :
msg["login.titre"]

L'utilisation des quotes simples ou doubles est équivalente car il faut les imbriquer par exemple lors de leur utilisation comme valeur de l'attribut d'un composant.

Exemple :
<h:inputText value='#{login["nom"]}'/>
<h:inputText value="#{login['nom]'}"/>

Attention, la syntaxe utilisée par JSF est proche mais différente de celle proposée par JSTL : JSF utilise le délimiteur #{ ... } et JSTL utilise le délimiteur ${ ... } .

JSF définit un ensemble de variables prédéfinies et utilisables dans les expressions de liaison de données :

Variable Rôle
header une collection de type Map encapsulant les éléments définis dans les paramètres de l'en-tête de la requête http (seule la première valeur est renvoyée)
header-values une collection de type Map encapsulant les éléments définis dans les paramètres de l'en-tête de la requête http (toutes les valeurs sont renvoyées sous la forme d'un tableau)
param une collection de type Map encapsulant les éléments définis dans les paramètres de la requête http (seule la première valeur est renvoyée)
param-values une collection de type Map encapsulant les éléments définis dans les paramètres de la requête http (toutes les valeurs sont renvoyées sous la forme d'un tableau)
cookies une collection de type Map encapsulant les éléments définis dans les cookies
initParam une collection de type Map encapsulant les éléments définis dans les paramètres d'initialisation de l'application
requestScope une collection de type Map encapsulant les éléments définis dans la portée request
sessionScope une collection de type Map encapsulant les éléments définis dans la portée session
applicationScope une collection de type Map encapsulant les éléments définis dans la portée application
facesContext une instance de la classe FacesContext
View une instance de la classe UIViewRoot qui encapsule la vue

Lorsque qu'une variable est utilisée dans une expression, JSF recherche dans la liste des variables prédéfinies, puis recherche une instance dans la portée request, puis dans la portée session et enfin dans la portée application. Si aucune instance n'est trouvée, alors JSF crée une nouvelle instance en tenant compte des informations du fichier de configuration. Cette instanciation est réalisée par un objet de type VariableResolver de l'application.

La syntaxe des expressions possède aussi quelques opérateurs :

Opérateurs Rôle Exemple
+ - * / % div mod opérateurs arithmétiques  
< <= > >= == !=
lt le gt ge eq ne
opérateurs de comparaisons  
&& || !
and or not
opérateurs logiques <h:inputText rendered="#{!monBean.affichable}" />
Empty opérateur vide : un objet null, une chaîne vide, un tableau ou une collection sans élément  
? : opérateur ternaire de test  

Il est possible de concaténer les résultats des évaluations de plusieurs expressions simplement en les plaçant les uns à la suite des autres.

Exemple :
<h:outputText value="#{messages.salutation}, #{utilisateur.nom}!"/>

Il est parfois nécessaire d'évaluer une expression dans le code des objets métiers pour obtenir sa valeur. Comme tous les composants sont stockés dans le FaceContext, il est possible d'accéder à cet objet pour obtenir les informations désirées. Il est d'abord nécessaire d'obtenir l'instance courante de l'objet FaceContext en utilisant la méthode statique getCurrentInstance().

Exemple :
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding = context.getApplication().createValueBinding("#{login.nom}");
String nom = (String) binding.getValue(context);

 

76.6.3. Les Backing beans

Les beans de type backing bean sont spécialement utilisés avec JSF pour encapsuler tout ou partie des composants d'une page et ainsi faciliter leur accès notamment lors des traitements.

Ces beans sont particulièrement utiles durant des traitements réalisés lors de validations ou de gestion d'événements car ils permettent un accès aux composants dont ils possèdent une référence.

Exemple :
package com.jmd.test.jsf;

import javax.faces.component.UIInput;

public class LoginBean {

  private UIInput composantNom; 
  private String nom;
  private String mdp;

  public UIInput getComposantNom() {
    return composantNom; 
  }

  public void setComposantNom(UIInput input) {
    composantNom = input;
  }

  public String getNom() {
    return nom;
  }
...
}

Dans la vue, il est nécessaire de lier un composant avec son attribut correspondant dans le backing bean. L'attribut binding d'un composant permet de réaliser cette liaison.

Exemple :
<h:inputText value="#{login.nom}" binding="#{login.composantNom}" />

 

76.7. Les composants pour les interfaces graphiques

JSF propose un ensemble de composants serveurs pour faciliter le développement d'interfaces graphiques utilisateur.

Pour les composants, JSF propose :

  • un ensemble de classes qui gèrent le comportement et l'état d'un composant
  • un modèle pour assurer le rendu du composant pour un type d'application (par exemple HTML)
  • un modèle de gestion des événements émis par le composant reposant sur le modèle des listeners
  • la possibilité d'associer à un composant un composant de conversion de données ou de validation des données

Tous ces composants héritent de la classe abstraite UIComponentBase.

JSF propose 12 composants de base :

UICommand Composant qui permet de réaliser une action émettant un événement
UIForm Composant qui regroupe d'autres composants dont l'état sera renvoyé au serveur
UIGraphic Composant qui représente une image
UIInput Composant qui permet de saisir des données
UIOutput Composant qui permet d'afficher des données
UIPanel Composant qui regroupe d'autres composants à afficher sous la forme d'un tableau
UIParameter  
UISelectItem Composant qui représente un élément sélectionné dans un ensemble
UISelectItems Composant qui représente un ensemble d'éléments
UISelectBoolean Composant qui permet de sélectionner parmi deux états
UISelectMany Composant qui permet de sélectionner plusieurs éléments d'un ensemble
UISelectOne Composant qui permet de sélectionner un seul élément d'un ensemble

Ces classes sont des javabeans qui définissent les fonctionnalités de base des composants permettant la saisie et la sélection de données.

Chacun de ces composants possède un type, un identifiant, une ou plusieurs valeurs locales et des attributs. Ils sont extensibles et il est même possible de créer ses propres composants.

Le comportement de ces composants repose sur le traitement d'événements respectant le modèle de gestion des événements de JSF.

Ces classes ne sont pas utilisées directement : elles sont utilisées par la bibliothèque de tags personnalisés qui se charge de les instancier et de leur associer le modèle de rendu adéquat.

Ces classes ne prennent pas en charge le rendu du composant. Par exemple, un objet de type UICommand peut être rendu en HTML sous la forme d'un lien hypertexte ou d'un bouton de formulaire.

 

76.7.1. Le modèle de rendu des composants

Pour chaque composant, il est possible de définir un ou plusieurs modèles qui se chargent du rendu de ce composant dans un contexte client particulier (par exemple HTML).

L'association entre un composant et son modèle de rendu est réalisée dans un RenderKit : il précise pour chaque composant le ou les modèles de rendu à utiliser. Par exemple, un objet de type UISelectOne peut être rendu sous la forme d'un ensemble de boutons radio, d'une liste ou d'une liste déroulante. Chacun de ces rendus est défini par un objet de type Renderer.

L'implémentation de référence propose un seul modèle de rendu pour les composants qui génèrent de l'HTML.

Ce modèle favorise la séparation entre l'état, le comportement d'un composant et sa représentation finale.

Le modèle de rendu permet de définir la représentation visuelle des composants. Chaque composant peut être rendu de plusieurs façons avec plusieurs modèles de rendu. Par exemple, un composant de type UICommand peut être rendu sous la forme d'un bouton ou d'un lien hypertexte. Le rendu peut être HTML mais il est possible d'utiliser un autre système de rendu comme XML ou WML.

Le modèle de rendu met un oeuvre un ou plusieurs kits de rendus.

 

76.7.2. L'utilisation de JSF dans une JSP

Pour une utilisation dans une JSP, l'implémentation de référence propose deux bibliothèques de tags personnalisés :

  • core : cette bibliothèque contient des fonctionnalités de bases ne générant aucun rendu. L'utilisation de cette bibliothèque est obligatoire car elle contient notamment l'élément view
  • html : cette bibliothèque se charge des composants avec un rendu en HTML

Pour utiliser ces deux bibliothèques, il est nécessaire d'utiliser une directive taglib pour chacune d'elles au début de la page JSP.

Exemple :
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

Le préfixe est libre mais par convention ce sont ceux fournis dans l'exemple qui sont utilisés.

Le tag <view> est obligatoire dans toutes pages utilisant JSF. Cet élément va contenir l'état de l'arborescence des composants de la page si l'application est configurée pour stocker l'état sur le client.

Le tag <form> génère un tag HTML form qui définit un formulaire.

Exemple :
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<head>
  <title>Application de tests avec JSF</title>
</head>
<body>
  <h:form>
  ...
  </h:form>
</body>
</f:view>
</html>

 

76.8. La bibliothèque de tags Core

Cette bibliothèque est composée de 18 tags.

Tag Rôle
actionListener ajouter un listener pour une action sur un composant
attribute ajouter un attribut à un composant
convertDateTime ajouter un convertisseur de type DateTime à un composant
convertNumber ajouter un convertisseur de type numérique à un composant
facet définit un élément particulier d'un composant
loadBundle charger un fichier contenant les chaînes de caractères d'une locale dans une collection de type Map
param ajouter un paramètre à un composant
selectitem définir l'élément sélectionné dans un composant permettant de faire un choix
selectitems définir les éléments sélectionnés dans un composant permettant de faire un choix
subview définir une sous-vue
verbatim ajouter un texte brut à la vue
view définir une vue
validator ajouter un validateur à un composant
validateDoubleRange ajouter un validateur de type « plage de valeurs réelles » à un composant
validateLength ajouter un validateur de type « taille de la valeur » à un composant
validateLongRange ajouter un validateur de type « plage de valeurs entières » à un composant
valueChangeListener ajouter un listener pour un changement de valeur sur un composant

La plupart de ces tags permettent d'ajouter des objets à un composant. Leur utilisation sera détaillée tout au long de ce chapitre.

 

76.8.1. Le tag <selectItem>

Ce tag représente un élément dans un composant qui peut en contenir plusieurs.

Les attributs de base sont les suivants :

Attribut Rôle
itemValue contient la valeur de l'élément
itemLabel contient le libellé de l'élément
itemDescription contient une description de l'élément (utilisé uniquement par les outils de développement)
itemDisabled contient l'état de l'élément
binding contient le nom d'une méthode qui renvoie un objet de type javax.faces.model.SelectItem
id contient l'identifiant du composant
value contient une expression qui désigne un objet de type javax.faces.model.SelectItem

Exemple :
<f:selectItem value="#{test.elementSelectionne}"/>

L'attribut value attend en paramètre une expression désignant une méthode qui renvoie un objet de type SelectItem. Cet objet encapsule l'objet de la liste qui sera sélectionné.

Exemple :
...
public SelectItem getElementSelectionne() {
  return new SelectItem("Element 1");
}
...

La classe SelectItem possède quatre constructeurs qui permettent de définir les différentes propriétés qui composent l'élément.

 

76.8.2. Le tag <selectItems>

Ce tag représente une collection d'éléments dans un composant qui peut en contenir plusieurs.

Ce tag est particulièrement utile car il évite d'utiliser autant de tags selectItem que d'éléments à définir.

Exemple :
...
<h:selectOneRadio>
  <f:selectItems value="#{test.listeElements}"/>
</h:selectOneRadio>
...

La collection d'objets de type SelectItem peut être soit une collection soit un tableau.

Exemple : avec un tableau d'objects de type SelectItem
package com.jmd.test.jsf;

import javax.faces.model.SelectItem;

public class TestBean {

  private SelectItem[] elements = {
    new SelectItem(new Integer(1), "Element 1"),
    new SelectItem(new Integer(2), "Element 2"),
    new SelectItem(new Integer(3), "Element 3"),
    new SelectItem(new Integer(4), "Element 4"),
  };

  public SelectItem[] getListeElements() {
    return elements;
  }

...
}

La collection peut être de type Map : dans ce cas le framework associe la clé de chaque occurrence à la propriété itemValue et la valeur à la propriété itemLabel

Exemple :
package com.jmd.test.jsf;

import java.util.HashMap;
import java.util.Map;

import javax.faces.model.SelectItem;

public class TestBean {

  private Map elements = null;

  public Map getListeElements() {
    if (elements == null) {
      elements = new HashMap();
      elements.put("Element 1", new Integer(1));
      elements.put("Element 2", new Integer(2));
      elements.put("Element 3", new Integer(3));
      elements.put("Element 4", new Integer(4));
    }
    return elements;
  }

  public SelectItem getElementSelectionne() {
    return new SelectItem("Element 1");
  }

  ...
}

 

76.8.3. Le tag <verbatim>

Ce tag permet d'insérer du texte dans la vue.

Son utilisation est obligatoire dans le corps des tags JSF pour insérer autre chose qu'un tag JSF. Par exemple, pour insérer un tag HTML dans le corps d'un tag JSF, il est obligatoire d'utiliser le tag <verbatim>.

Les tags suivants peuvent avoir un corps : commandLink, outputLink, panelGroup, panelGrid et dataTable.

Exemple :
<h:outputLink value="http://java.sun.com" title="Java">
  <f:verbatim>
    Site Java de Sun
  </f:verbatim>
</h:outputLink>

Il est possible d'utiliser le tag <outputText> à la place du tag <verbatim>.

 

76.8.4. Le tag <attribute>

Ce tag permet de fournir un attribut quelconque à un composant puisque chaque composant peut stocker des attributs arbitraires.

Ce tag possède deux attributs :

Attribut Rôle
Name nom de l'attribut
Value valeur de l'attribut

Dans le code d'un composant, il est possible d'utiliser la méthode getAttributes() pour obtenir une collection de type Map des attributs du composant.

Ceci offre un mécanisme souple pour fournir des paramètres sans être obligé de créer un nouveau composant ou de modifier un composant existant en lui ajoutant un ou plusieurs attributs.

 

76.8.5. Le tag <facet>

Ce tag permet de définir des éléments particuliers d'un composant.

Il est par exemple utilisé pour définir les lignes d'en-tête et de pied de page des tableaux.

Ce tag possède plusieurs attributs :

Attribut Rôle
Name Permet de préciser le type de l'élément généré par le tag
Les valeurs possibles sont header et footer

Exemple :
<h:column>
  <f:facet name="header">
    <h:outputText value="Nom" />
  </f:facet>
  <h:outputText value="#{personne.nom}"/>
</h:column>

 

76.9. La bibliothèque de tags Html

Cette bibliothèque est composée de 25 tags qui permettent la réalisation de l'interface graphique de l'application.

Tag Rôle
form le tag <form> HTML
commandButton un bouton
commandLink un lien qui agit comme un bouton
graphicImage une image
inputHidden une valeur non affichée
inputSecret une zone de saisie de texte monoligne dont la valeur n'est pas lisible
inputText une zone de saisie de texte monoligne
inputTextarea une zone de saisie de texte multiligne
outputLink un lien
outputFormat du texte affiché avec des valeurs fournies en paramètre
outputText du texte affiché
panelGrid un tableau
panelGroup un panneau permettant de regrouper plusieurs composants
selectBooleanCheckbox une case à cocher
selectManyCheckbox un ensemble de cases à cocher
selectManyListbox une liste déroulante où plusieurs éléments sont sélectionnables
selectManyMenu un menu où plusieurs éléments sont sélectionnables
selectOneListbox une liste déroulante où un seul élément est sélectionnable
selectOneMenu un menu où un seul élément est sélectionnable
selectOneRadio un ensemble de boutons radio
dataTable une grille proposant des fonctionnalités avancées
column une colonne d'une grille
message le message d'erreur lié à un composant
messages les messages d'erreur liés à tous les composants

 

76.9.1. Les attributs communs

Ces tags possèdent des attributs communs pouvant être regroupés en trois catégories :

  • les attributs de base
  • les attributs liés à HTML
  • les attributs liés à JavaScript

Chaque tag utilise ou non chacun de ces attributs.

Les attributs de base sont les suivants :

Attribut Rôle
id contient l'identifiant du composant
binding permet l'association avec un backing bean
rendered contient un booléen qui indique si le composant doit être affiché
styleClass contient le nom d'une classe CSS à appliquer au composant
value contient la valeur du composant
valueChangeListener permet l'association à une méthode qui va traiter les changements de valeurs
converter contient une classe de conversion des données de chaîne de caractères en objet et vice versa
validator contient une classe de validation des données
required contient un booléen qui indique si une valeur doit obligatoirement être saisie

L'attribut id est très important car il permet d'avoir accès :

  • au tag dans le code de la vue par d'autres tags
    <h:inputText id="nom" required="true"/>

<h:message for="nom"/>

  • au tag dans le code JavaScript de la vue
  • dans le code Java des objets métiers.
    UIComponent component = event.getComponent().findComponent("nomComposant");

L'attribut binding permet d'associer le composant avec un champ d'une classe de type bean. Un tel bean est nommé backing bean dans une application JSF.

Exemple :
...
<h:inputText value="#{login.nom}" id="nom" required="true" binding="#{login.inputTextNom}"/>
..

...
import javax.faces.component.UIComponent;

public class LoginBean {
  private String nom;

  private UIComponent inputTextNom;

  public UIComponent getInputTextNom() {
    return inputTextNom;
  }

  public void setInputTextNom(UIComponent inputTextNom) {
    this.inputTextNom = inputTextNom;
  }
...

L'attribut value permet de préciser la valeur d'un tag. Cette valeur peut être fournie sous deux formes :

  • en dur dans le code :
    <h:outputText value="Bonjour"/>
  • en utilisant une expression de liaison de données :
    <h:inputText value="#{login.nom}"/>

L'attribut converter permet de préciser une classe qui va convertir la valeur d'un objet en chaîne de caractères et vice versa. L'utilisation de cet attribut est détaillée dans une des sections suivantes.

L'attribut validator permet de préciser une classe qui va réaliser des contrôles de validation sur la valeur saisie. L'utilisation de cet attribut est détaillée dans une des sections suivantes.

L'attribut styleClass permet de préciser le nom d'un style défini dans une feuille de style CSS qui sera appliqué au composant.

Exemple : le fichier monstyle.css
.titre {
color:red;
}

Dans la vue, il faut inclure la feuille de style dans la partie en-tête de la page HTML.

Exemple :
...
<link href="monstyle.css" rel="stylesheet" type="text/css"/>
...
<h:outputText value="#{msg.login_titre}" styleClass="titre"/>
...

L'attribut renderer permet de préciser si le composant sera affiché ou non dans la vue. La valeur de l'attribut peut être obtenue dynamiquement par l'utilisation du langage d'expression.

Exemple :
<h:panelGrid rendered='#{listepersonnes.nbOccurrences gt 0}'/>

Les principaux attributs liés à HTML sont les suivants :

Attribut Rôle
accesskey contient le raccourci clavier pour donner le focus au composant
alt contient le texte alternatif pour les composants non textuels
border contient la taille de la bordure en pixel
disabled permet de désactiver le composant
maxlength contient le nombre maximum de caractères saisis
readonly permet de rendre une zone de saisie en lecture seule
rows contient le nombre de lignes visibles pour une zone de saisie multiligne
shape contient la définition d'une région
size contient la taille de la zone de saisie
style contient le style CSS à utiliser
target contient le nom de la frame cible pour l'affichage de la page
title contient le titre du composant généralement transformé en une bulle d'aide
width contient la largeur du composant

Le rôle de la plupart de ces tags est identique à leurs homologues définis dans HTML 4.0.

L'attribut style permet de définir un style CSS qui sera appliqué au composant. Cet attribut contient directement la définition du style à la différence de l'attribut styleClass qui contient le nom d'une classe CSS définie dans une feuille de style. Il est préférable d'utiliser l'attribut styleClass plutôt que l'attribut style afin de faciliter la maintenance de la charte graphique.

Exemple :
<h:outputText value="#{login.nom}" style="color:red;"/>

Les attributs liés à JavaScript sont :

Attribut Rôle
onblur perte du focus
onchange changement de la valeur
onclick clic du bouton de la souris sur le composant
ondblclick double-clic du bouton de la souris sur le composant
onfocus réception du focus
onkeydown une touche est enfoncée
onkeypress appui sur une touche
onkeyup une touche est relachée
onmousedown le bouton de la souris est enfoncé
onmousemove déplacement du curseur de la souris sur le composant
onmouseout déplacement du cuseur de la souris hors du composant
onmouseover passage de la souris au-dessus du composant
onmouseup le bouton de la souris est relaché
onreset réinitialisation du formulaire
onselect sélection du texte dans une zone de saisie
onsubmit soumission du formulaire

 

76.9.2. Le tag <form>

Ce tag représente un formulaire HTML.

Il possède les attributs suivants :

Attribut Rôle
binding, id, rendered, styleClass attributs communs de base
accept, acceptcharset, dir, enctype, lang, style, target, title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onreset, onsubmit  attributs communs liés aux événements JavaScript

Il est préférable de définir explicitement l'attribut id pour permettre son exploitation notamment dans le code JavaScript, sinon un id est généré automatiquement.

Ceci est d'autant plus important que les id des composants intégrés dans le formulaire sont préfixés par l'id du formulaire suivi du caractère deux-points. Il faut tenir compte de ce point lors de l'utilisation de code JavaScript faisant référence à un composant.

 

76.9.3. Les tags <inputText>, <inputTextarea>, <inputSecret>

Ces trois tags permettent de générer des composants pour la saisie de données.

Les attributs de ces tags sont les suivants :

Attribut Rôle
cols définir le nombre de colonnes (pour le composant inputTextarea uniquement)
immediate permettre de demander d'ignorer les étapes de validation des données
redisplay permettre de réafficher le contenu lors du réaffichage de la page (pour le composant inputSecret uniquement)
required rendre obligatoire la saisie d'une valeur
rows définir le nombre de lignes affichées (pour le composant inputTextarea uniquement)
valueChangeListener préciser une classe de type listener lors du changement de la valeur
binding, converter, id, rendered, required, styleClass, value, validator attributs communs de base
accesskey, alt, dir, disabled,lang, maxlength, readonly, size, style, tabindex, title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onselect attributs communs liés aux événements JavaScript

Exemple :
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<head>
  <title>Saisie des données</title>
</head>
<body>
  <h:form>
    <h3>Saisie des données</h3>
    <p><h:inputText size="20" /></p>
    <p><h:inputTextarea rows="3" cols="20" /></p>
    <p><h:inputSecret size="20" /></p>
  </h:form>
</body>
</f:view>
</html>

Résultat

 

76.9.4. Le tag <ouputText> et <outputFormat>

Ces deux tags permettent d'insérer dans la vue une valeur sous la forme d'une chaîne de caractères. Par défaut, ils ne génèrent pas de tags HTML mais insèrent simplement la valeur dans la vue sauf si un style CSS est précisé avec l'attribut style ou styleClass. Dans ce cas, la valeur est contenue dans un tag HTML <span>.

Les attributs de ces deux tags sont :

Attribut Rôle
escape booléen qui précise si certains caractères de la valeur seront encodés ou non. La valeur par défaut est false.
binding, converter, id, rendered, styleClass, value attributs communs de base
style, title attributs communs liés à HTML

L'attribut escape est particulièrement utile pour encoder certains caractères spéciaux avec leur code HTML correspondant.

Exemple :
<h:outputText escape="true" value="Nombre d'occurrences > 200"/>

Le tag outputText peut être utilisé pour générer du code HTML en valorisant l'attribut escape à false.

Exemple :
<p><h:outputText escape="false" value="<H2>Saisie des données</H2>"/></p> 

<p><h:outputText escape="true" value="<H2>Saisie des données</H2>"/></p>

Résultat :

Le tag outputFormat permet de formater une chaîne de caractères avec des valeurs fournies en paramètres.

Exemple :
<p>
  <h:outputFormat value="La valeur doit être entre {0} et {1}.">
    <f:param value="1"/>
    <f:param value="9"/>
  </h:outputFormat>
</p>

Résultat :

Ce composant utilise la classe java.text.MessageFormat pour formater le message. L'attribut value doit donc contenir une chaîne de caractères utilisable par cette classe.

Le tag <param> permet de fournir la valeur de chacun des paramètres.

 

76.9.5. Le tag <graphicImage>

Ce composant représente une image : il génère un tag HTML <img>.

Les attributs de ce tag sont les suivants :

Attribut Rôle
binding, id, rendered, styleClass, value Attributs communs de base
alt, dir, height, ismap, lang, longdesc, style, title, url, usemap, width Attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup Attributs communs liés aux événements JavaScript

Les attributs value et URL peuvent préciser l'URL de l'image.

Exemple :
<p><h:graphicImage value="/images/erreur.jpg" /></p>
<p><h:graphicImage url="/images/warning.jpg" /></p>

Résultat :

 

76.9.6. Le tag <inputHidden>

Ce composant représente un champ caché dans un formulaire.

Les attributs sont les suivants :

Attribut Rôle
binding, converter, id, immediate, required, validator, value, valueChangeListener attributs communs de base

Exemple :
<h:inputHidden value="#{login.nom}" />

Résultat dans le code HTML:
...
	  <input type="hidden" name="_id0:_id12" value="test" />
...

 

76.9.7. Le tag <commandButton> et <commandLink>

Ces composants représentent respectivement un bouton de formulaire et un lien qui déclenche une action. L'action demandée sera traitée par le framework JSF.

Les attributs sont les suivants :

Attribut Rôle
action peut être une chaîne de caractères ou une méthode qui renvoie une chaîne de caractères qui sera traitée par le navigation handler.
actionListener précise une méthode possédant une signature void nomMethode(ActionEvent) qui sera exécutée lors d'un clic
image URL tenant compte du contexte de l'application pour l'image qui sera utilisée à la place du bouton (uniquement pour le tag commandButton)
type type de bouton généré : button, submit, reset (uniquement pour le tag commandButton)
value le texte affiché par le bouton ou le lien
accesskey, alt, binding, id, lang, rendered, styleClass attributs communs de base
coords (uniquement pour le tag commandLink), dir, disabled, hreflang (uniquement pour le tag commandLink), lang, readonly, rel (uniquement pour le tag commandLink), rev (uniquement pour le tag commandLink), shape (uniquement pour le tag commandLink), style, tabindex, target (uniquement pour le tag commandLink), title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect attributs communs liés aux événements JavaScript

Il est possible d'insérer dans le corps du tag <commandLink> d'autres composants qui feront partie intégrante du lien comme par exemple du texte ou une image.

Exemple :
<p>
  <h:commandLink>
    <h:outputText value="Valider"/>
  </h:commandLink>
</p>
<p>
  <h:commandLink>
    <h:graphicImage value="/images/oeil.jpg"/>
  </h:commandLink>
</p>

Résultat :

Il est aussi possible de fournir un ou plusieurs paramètres qui seront envoyés dans la requête en utilisant le tag <param> dans le corps du tag

Exemple :
<h:commandLink>
  <h:outputText value="Selectionner"/>
  <f:param name="id" value="1"/>
</h:commandLink>

 

Résultat dans le page HTML générée :
<a href="#" onclick="document.forms['_id0']['_id0:_idcl'].value='_id0:_id15'; 
	  document.forms['_id0'].submit(); return false;">
	mg src="/test_JSF/images/oeil.jpg" alt="" /></a>

Le tag <commandLink> génère du code JavaScript dans la vue pour soumettre le formulaire lors d'un clic.

 

76.9.8. Le tag <ouputLink>

Ce composant représente un lien direct vers une ressource dont la demande ne sera pas traitée par le framework JSF.

Les attributs sont les suivants :

Attribut Rôle
accesskey, binding, converter, id, lang, rendered, styleClass, value attributs communs de base
charset, coords, dir, hreflang, lang, rel, rev, shape, style, tabindex, target, title, type attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup attributs communs liés aux événements JavaScript

L'attribut value doit contenir l'URL qui sera utilisée dans l'attribut href du lien HTML. Si le premier caractère est un # (dièse) alors le lien pointe vers une ancre définie dans la même page.

Il est possible d'insérer dans le corps du tag ouputLink d'autres composants qui feront partie intégrante du lien.

Exemple :
<p>
  <h:outputLink value="http://java.sun.com">
    <h:graphicImage value="/images/java.jpg"/>
  </h:outputLink>
</p>

Résultat :

Le code HTML généré dans la page est le suivant :

<a href="http://java.sun.com"><img src="/test_JSF/images/java.jpg" alt="" /></a>

Attention, pour mettre du texte dans le corps du tag, il est nécessaire d'utiliser un tag verbatim ou outputText.

Exemple :
<p>
  <h:outputLink value="http://java.sun.com" title="Java">
    <f:verbatim>
       Site Java de Sun
    </f:verbatim>
  </h:outputLink>
</p>

 

76.9.9. Les tags <selectBooleanCheckbox> et <selectManyCheckbox>

Ces composants représentent respectivement une case à cocher et un ensemble de cases à cocher.

Les attributs sont les suivants :

Attribut Rôle
disabledClass classe CSS pour les éléments non sélectionnés
(pour le tag selectManyCheckbox uniquement)
enabledClass classe CSS pour les éléments sélectionnés
(pour le tag selectManyCheckbox uniquement)
layout préciser la disposition des éléments
(pour le tag selectManyCheckbox uniquement)
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener attributs communs de base
accesskey, border, dir, disabled, lang, readonly, style, tabindex, title attributs communs liés à HTML
(border pour le tag selectManyCheckbox uniquement)
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect attributs communs liés aux événements JavaScript

L'attribut layout permet de préciser la disposition des cases à cocher : lineDirection pour une disposition horizontale (c'est la valeur par défaut) et pageDirection pour une disposition verticale.

Le tag <selectBooleanCheckbox> dont la valeur peut être associée à une propriété booléenne d'un bean représente une case à cocher simple.

Exemple :
<h:selectBooleanCheckbox value="#{saisieOptions.recevoirLettre}">
</h:selectBooleanCheckbox> Recevoir la lettre d'information

Résultat :

Pour gérer l'état du composant, il faut utiliser l'attribut value en lui fournissant la valeur d'une propriété booléen d'un backing bean.

Exemple :
public class SaisieOptions {

  private boolean recevoirLettre;

  public void setRecevoirLettre(boolean valeur) {
    recevoirLettre = valeur;
  }

  public boolean getRecevoirLettre() {
    return recevoirLettre; 
  }
...

Le tag <selectManyCheckbox> représente un ensemble de cases à cocher. Dans cet ensemble, il est possible d'en sélectionner une ou plusieurs.

Chaque case à cocher est définie par un tag selectItem dans le corps du tag selectManyCheckbox.

Exemple :
<h:selectManyCheckbox layout="pageDirection">
  <f:selectItem itemValue="petit" itemLabel="Petit" />
  <f:selectItem itemValue="moyen" itemLabel="Moyen" />
  <f:selectItem itemValue="grand" itemLabel="Grand" />
  <f:selectItem itemValue="tresgrand" itemLabel="Tres grand" />
</h:selectManyCheckbox>

Résultat :

Le rendu du composant est un tableau HTML dont chaque cellule contient une case à cocher encapsulée dans un tag HTML <label> :

Exemple :
<table>
 <tr>
  <td>
   <label><input name="_id0:_id1" value="petit" type="checkbox"> Petit</input></label></td>
 </tr>
 <tr>
  <td>
   <label><input name="_id0:_id1" value="moyen" type="checkbox"> Moyen</input></label></td>
 </tr>
 <tr>
  <td>
   <label><input name="_id0:_id1" value="grand" type="checkbox"> Grand</input></label></td>
 </tr>
 <tr>
  <td>
    <label><input name="_id0:_id1" value="tresgrand" type="checkbox"> Tres grand</input>
    </label></td>
 </tr>
</table>

 

76.9.10. Le tag <selectOneRadio>

Ce composant représente un ensemble de boutons radio dont un seul peut être sélectionné.

Les attributs sont les suivants :

Attribut Rôle
disabledClass classe CSS pour les éléments non sélectionnés
enabledClass classe CSS pour les éléments sélectionnés
layout préciser la disposition des éléments
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener Attributs communs de base
accesskey, border, dir, disabled, lang, readonly, style, tabindex, title Attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect Attributs communs liés aux événements JavaScript

Les éléments peuvent être précisés un par un avec le tag <selectItem>.

Exemple :
<h:selectOneRadio layout="pageDirection">
  <f:selectItem itemValue="petit" itemLabel="Petit" />
  <f:selectItem itemValue="moyen" itemLabel="Moyen" />
  <f:selectItem itemValue="grand" itemLabel="Grand" />
  <f:selectItem itemValue="tresgrand" itemLabel="Tres grand" />
</h:selectOneRadio>

Résultat :

Le rendu du composant est un tableau HTML dont chaque cellule contient un bouton radio encapsulé dans un tag HTML <label> :

Exemple :
<table>
  <tr>
    <td>
      <label><input type="radio" name="_id0:_id1" value="petit"> Petit</input></label></td>
  </tr>
  <tr>
    <td>
      <label><input type="radio" name="_id0:_id1" value="moyen"> Moyen</input></label></td>
  </tr>
  <tr>
    <td>
      <label><input type="radio" name="_id0:_id1" value="grand"> Grand</input></label></td>
  </tr>
  <tr>
    <td>
      <label><input type="radio" name="_id0:_id1" value="tresgrand"> Tres grand</input>
	  </label></td>
  </tr>
</table>

Les éléments peuvent être précisés sous la forme d'un tableau de type SelecItem avec le tag <selectItems>.

Exemple :
<h:selectOneRadio value="#{saisieOptions.taille}" layout="pageDirection" id="taille">
<f:selectItems value="#{saisieOptions.tailleItems}"/>
</h:selectOneRadio>

Dans ce cas, le bean doit contenir au moins deux méthodes : getTaille() pour renvoyer la valeur de l'élément sélectionné et getTailleItems() qui renvoie un tableau d'objets de type SelectItems contenant les éléments.

Exemple :
package com.jmd.test.jsf;

import javax.faces.model.*;

public class SaisieOptions {

  private Integer taille = null;

  private SelectItem[] tailleItems = {
    new SelectItem(new Integer(1), "Petit"),
    new SelectItem(new Integer(2), "Moyen"),
    new SelectItem(new Integer(3), "Grand"),
    new SelectItem(new Integer(4), "Très grand") };

  public SaisieOptions() {
    taille = new Integer(2);
  }

  public Integer getTaille() {
    return taille;
  }

  public void setTaille(Integer newValue) {
    taille = newValue;
  }

  public SelectItem[] getTailleItems() {
    return tailleItems;
  }
}

Le bean doit être déclaré dans le fichier faces-config.xml

Exemple :
<managed-bean>
  <managed-bean-name>saisieOptions</managed-bean-name>
  <managed-bean-class>com.jmd.test.jsf.SaisieOptions</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Résultat :

 

76.9.11. Le tag <selectOneListbox>

Ce composant représente une liste d'éléments dont un seul peut être sélectionné.

Les attributs sont les suivants :

Attribut Rôle
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener attributs communs de base
accesskey, dir, disabled, lang, readonly, style, size, tabindex, title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect attributs communs liés aux événements JavaScript

L'attribut size permet de préciser le nombre d'éléments de la liste affichée.

Exemple :
<h:selectOneListbox value="#{saisieOptions.taille}">
  <f:selectItems value="#{saisieOptions.tailleItems}"/>
</h:selectOneListbox>

Résultat :

 

76.9.12. Le tag <selectManyListbox>

Ce composant représente une liste d'éléments dont plusieurs peuvent être sélectionnés.

Les attributs sont les suivants :

Attribut Rôle
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener attributs communs de base
accesskey, dir, disabled, lang, readonly, style, size, tabindex, title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect attributs communs liés aux événements JavaScript

Exemple :
<h:selectManyListbox value="#{saisieOptions.legumes}">
  <f:selectItems value="#{saisieOptions.legumesItems}"/>
</h:selectManyListbox>

La liste des éléments sélectionnés doit pouvoir contenir zéro ou plusieurs valeurs sous la forme d'un tableau ou d'une liste.

Exemple :
package com.jmd.test.jsf;

import javax.faces.model.*;

public class SaisieOptions {

  private String[] legumes = {
    "navets", "choux" };
  private SelectItem[] legumesItems = {
    new SelectItem("epinards", "Epinards"),
    new SelectItem("poireaux", "Poireaux"),
    new SelectItem("navets", "Navets"),
    new SelectItem("flageolets", "Flageolets"),
    new SelectItem("choux", "Choux"),
    new SelectItem("aubergines", "Aubergines") };

  public SaisieOptions() {
  }

  public String[] getLegumes() {
    return legumes;
  }

  public SelectItem[] getLegumesItems() {
    return legumesItems;
  }
}

Résultat :

Il est possible d'utiliser un objet de type List à la place des tableaux.

Exemple :
package com.jmd.test.jsf;

import javax.faces.model.*;
import java.util.*;

public class SaisieOptions {

  private List legumes = null;

  private List legumesItems = null;

  public List getLegumesItems() {
    if (legumesItems == null) {
      legumesItems = new ArrayList();
      legumesItems.add(new SelectItem("epinards", "Epinards"));
      legumesItems.add(new SelectItem("poireaux", "Poireaux"));
      legumesItems.add(new SelectItem("navets", "Navets"));
      legumesItems.add(new SelectItem("flageolets", "Flageolets"));
      legumesItems.add(new SelectItem("choux", "Choux"));
      legumesItems.add(new SelectItem("aubergines", "Aubergines"));
    }
    return legumesItems;
  }

  public List getLegumes() {
    return legumes;
  }

  public void setLegumes(List newValue) {
    legumes = newValue;
  }

  public SaisieOptions() {
    legumes = new ArrayList();
    legumes.add("navets");
    legumes.add("choux");
  }
}

 

76.9.13. Le tag <selectOneMenu>

Ce composant représente une liste déroulante dont un seul élément peut être sélectionné.

Les attributs sont les suivants :

Attribut Rôle
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener attributs communs de base
accesskey, dir, disabled, lang, readonly, style, tabindex, title attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect attributs communs liés aux événements JavaScript

Exemple :
<h:selectOneMenu value="#{saisieOptions.taille}">
<f:selectItems value="#{saisieOptions.tailleItems}"/>
</h:selectOneMenu>

Résultat :

Exemple : le code HTML généré
<select name="_id0:_id6" size="1"> <option value="1">Petit</option>
  <option value="2" selected="selected">Moyen</option>
  <option value="3">Grand</option>
  <option value="4">Tr&egrave;s grand</option>
</select>

 

76.9.14. Le tag <selectManyMenu>

Ce composant représente une liste d'éléments dont le rendu HTML est un tag select avec une seule option visible.

Les attributs sont les suivants :

Attribut Rôle
binding, converter, id, immediate, styleClass, required, rendered, validator, value, valueChangeListener Attributs communs de base
accesskey, dir, disabled, lang, readonly, style, tabindex, title Attributs communs liés à HTML
onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect Attributs communs liés aux événements JavaScript

Exemple :
<h:selectManyMenu value="#{saisieOptions.legumes}">
<f:selectItems value="#{saisieOptions.legumesItems}"/>
</h:selectManyMenu>

Résultat :

  

 

76.9.15. Les tags <message> et <messages>

Des messages peuvent être émis lors de traitements. Ils sont stockés dans le contexte de l'application JSF pour être restitués dans la vue. Ils permettent notamment de fournir des messages d'erreurs aux utilisateurs.

JSF définit quatre types de messages :

  • Information
  • Warning
  • Error
  • Fatal

Chaque message possède un résumé et un descriptif.

Le tag <messages> permet d'afficher tous les messages stockés dans le contexte de l'application JSF.

Le tag message permet d'afficher un seul message, le dernier ajouté, pour un composant donné.

Les attributs sont les suivants :

Attributs Rôle
errorClass nom d'une classe CSS pour un message de type error
errorStyle style CSS pour un message de type error
fatalClass nom d'une classe CSS pour un message de type fatal
fatalStyle style CSS pour un message de type fatal
globalOnly booléen qui permet de n'afficher que les messages qui ne sont pas associés à un composant. Par défaut, False (uniquement pour le tag messages)
infoClass nom d'une classe CSS pour un message de type Information
infoStyle style CSS pour un message de type Information
Layout format de la liste de messages : list ou table (uniquement pour le composant messages)
showDetail booléen qui précise si la description des messages est affichée ou non. Par défaut, false pour le tag message et true pour le tag messages
showSummary booléen qui précise si le résumé des messages est affiché ou non. Par défaut, true pour le tag message et false pour le tag messages
Tooltip booléen qui précise si la description est affichée sous la forme d'une bulle d'aide
warnClass nom d'une classe CSS pour un message de type Warning
warnStyle le style CSS pour un message de type Warning
For l'identifiant du composant pour lequel le message doit être affiché
binding, id, rendered, styleClass attributs communs de base
style, title attributs communs liés à HTML

 

76.9.16. Le tag <panelGroup>

Ce composant permet de regrouper plusieurs composants.

Les attributs sont les suivants :

Attribut Rôle
binding, id, rendered, styleClass attributs communs de base
style style CSS

Exemple :
<td bgcolor='#DDDDDD'>
  <h:panelGroup>
    <h:inputText value="#{login.nom}" id="nom" required="true"/>
    <h:message for="nom"/> 
  </h:panelGroup>
</td>

Résultat :

 

76.9.17. Le tag <panelGrid>

Ce composant représente un tableau HTML.

Les attributs sont les suivants :

Attribut Rôle
bgcolor couleur de fond du tableau
border taille de la bordure du tableau
cellpadding espacement intérieur de chaque cellule
cellspacing espacement extérieur de chaque cellule
columnClasses nom de classes CSS pour les colonnes. Il est possible de préciser plusieurs noms de classes qui seront utilisées sur chaque colonne
columns nombre de colonnes du tableau
footerClass nom de la classe CSS pour le pied du tableau
frame précise les règles pour le contour du tableau. Les valeurs possibles sont : none, above, below, hsides, vsides, lhs, rhs, box, border
headerClass nom de la classe CSS pour l'en-tête du tableau
rowClasses nom de classes CSS pour les lignes. Il est possible de préciser deux noms de classes séparés par une virgule qui seront utilisés alternativement sur chaque ligne
rules précise les règles de dessin des lignes entre les cellules. Les valeurs possibles sont : groups, rows, columns, all
summary résumé du tableau
binding, id, rendered, styleClass, value attributs communs de base
dir, lang, style, title, width attributs communs liés à HTML
onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup attributs communs liées aux événements JavaScript

Par défaut les composants sont insérés les uns à la suite des autres dans les cellules en partant de la gauche vers la droite et en passant à la ligne suivante si nécessaire.

Il est possible de ne mettre qu'un seul composant par cellule. Ainsi pour placer plusieurs composants dans une cellule, il faut les regrouper dans un tag panelGroup.

Exemple :
<h:panelGrid columns="2">
  <h:outputText value="Nom : " />
  <h:panelGroup>
    <h:inputText value="#{login.nom}" id="nom" required="true"/>
    <h:message for="nom"/>
  </h:panelGroup>
  <h:outputText value="Mot de passe :" />
  <h:inputSecret value="#{login.mdp}"/>
  <h:commandButton value="Login" action="login"/>
</h:panelGrid>

Résultat :

Le code HTML généré est le suivant :

Exemple : le code HTML généré
...
<table> 
 
  <tbody> 
    <tr> 
      <td>Nom : </td> 
      <td><input id="_id0:nom" type="text" name="_id0:nom" /></td> 
    </tr> 
    <tr> 
      <td>Mot de passe :</td> 
      <td><input type="password" name="_id0:_id6" value="" /></td> 
    </tr> 
    <tr> 
      <td><input type="submit" name="_id0:_id7" value="Login" /></td> 
    </tr> 
  </tbody> 
</table> 
...

 

76.9.18. Le tag <dataTable>

Ce composant représente un tableau HTML dans lequel des données vont pouvoir être automatiquement présentées. Ce composant est sûrement le plus riche en fonctionnalité et donc le plus complexe des composants fournis en standard.

Les attributs sont les suivants :

Attribut Rôle
bgcolor couleur de fond du tableau
border taille de la bordure du tableau
cellpadding espacement intérieur de chaque cellule
cellspacing espacement extérieur de chaque cellule
columnClasses nom de classes CSS pour les colonnes. Il est possible de préciser plusieurs noms de classes qui seront utilisées sur chaque colonne
first index de la première occurrence des données qui sera affichée dans le tableau
footerClass nom de la classe CSS pour le pied du tableau
frame précise les règles pour le contour du tableau. Les valeurs possibles sont : none, above, below, hsides, vsides, lhs, rhs, box, border
headerClass nom de la classe CSS pour l'en-tête du tableau
rowClasses nom de classes CSS pour les lignes. Il est possible de préciser deux noms de classes qui seront utilisées alternativement sur chaque ligne
rules précise les règles de dessin des lignes entre les cellules. Les valeurs possibles sont : groups, rows, columns, all
summary résumé du tableau
var nom de la variable qui va contenir l'occurrence en cours de traitement lors du parcours des données
binding, id, rendered, styleClass, value attributs communs de base
dir, lang, style, title, width attributs communs liés à HTML
onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup attributs communs liés aux événements JavaScript

Le tag <dataTable> parcourt les données et pour chaque occurrence, il crée une ligne dans le tableau.

L'attribut value représente une expression qui précise les données à utiliser. Ces données peuvent être sous la forme :

  • d'un tableau
  • d'un objet de type java.util.List
  • d'un objet de type java.sql.ResultSet
  • d'un objet de type javax.servlet.jsp.jstl.sql.Result
  • d'un objet de type javax.faces.model.DataModel

Pour chaque élément encapsulé dans les données, le tag dataTable crée une nouvelle ligne.

Quelque soit le type qui encapsule les données, le composant dataTable va les mapper dans un objet de type DataModel. C'est cet objet que le composant va utiliser comme source de données. JSF définit 5 classes qui héritent de la classe DataModel : ArrayDataModel, ListDataModel, ResultDataModel, ResultSetDataModel et ScalarDataModel.

La méthode getWrappedObject() permet d'obtenir la source de données fournie en paramètre de l'attribut value.

L'attribut item permet de préciser le nom d'une variable qui va contenir les données d'une occurrence.

Chaque colonne est définie grâce à un tag <column>.

Exemple :
<h:dataTable value="#{listePersonnes.personneItems}" var="personne" cellspacing="4">
  <h:column>
    <f:facet name="header">
      <h:outputText value="Nom" />
    </f:facet>
    <h:outputText value="#{personne.nom}"/>
  </h:column>

  <h:column>
    <f:facet name="header">
      <h:outputText value="Prenom" />
    </f:facet>
    <h:outputText value="#{personne.prenom}" />
  </h:column>

  <h:column>
    <f:facet name="header">
      <h:outputText value="Date de naissance" />
    </f:facet>
    <h:outputText value="#{personne.datenaiss}"/>
  </h:column>
  
  <h:column>
    <f:facet name="header">
      <h:outputText value="Poids" />
    </f:facet>
    <h:outputText value="#{personne.poids}"/>
  </h:column>
  
  <h:column>
    <f:facet name="header">
      <h:outputText value="Taille" />
    </f:facet>
    <h:outputText value="#{personne.taille}"/>
  </h:column>
  
</h:dataTable>

L'en-tête et le pied du tableau sont précisés avec un tag <facet> pour chacun dans chaque tag <column>.

Dans l'exemple précédent l'instance listePersonnes est une classe dont le code est le suivant :

Exemple :
package com.jmd.test.jsf;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

public class PersonnesBean {

  private List PersonneItems = null;

  public List getPersonneItems() {
    if (PersonneItems == null) {
      PersonneItems = new ArrayList();
      PersonneItems.add(new Personne("Nom1", "Prenom1",
	    new GregorianCalendar(1967, Calendar.OCTOBER, 22).getTime(),10,1.10f));
      PersonneItems.add(new Personne("Nom2", "Prenom2",
	    new GregorianCalendar(1972, Calendar.MARCH, 10).getTime(),20,1.20f));
      PersonneItems.add(new Personne("Nom3", "Prenom3",
	    new GregorianCalendar(1944, Calendar.NOVEMBER, 4).getTime(),30,1.30f));
      PersonneItems.add(new Personne("Nom4", "Prenom4",
	    new GregorianCalendar(1958, Calendar.JULY, 19).getTime(),40,1.40f));
      PersonneItems.add(new Personne("Nom5", "Prenom5",
	    new GregorianCalendar(1934, Calendar.JANUARY, 6).getTime(),50,1.50f));
      PersonneItems.add(new Personne("Nom6", "Prenom6",
	    new GregorianCalendar(1989, Calendar.DECEMBER, 12).getTime(),60,1.60f));
    }
    return PersonneItems;
  }
}

La méthode getPersonneItems() renvoie une collection d'objets de type Personne.

La classe Personne encapsule simplement les données d'une personne.

Exemple :
package com.jmd.test.jsf;

import java.util.Date;

public class Personne {
  private String nom;
  private String prenom;
  private Date datenaiss;
  private int poids;
  private float taille;
  private boolean supprime;
  
  public Personne(String nom, String prenom, Date datenaiss, int poids,
    float taille) {
    super();
    this.nom = nom;
    this.prenom = prenom;
    this.datenaiss = datenaiss;
    this.poids = poids;
    this.taille = taille;
    this.supprime = false;
  }
  
  public boolean isSupprime() {
    return supprime;
  }
  
  public void setSupprime(boolean supprimer) {
    supprime = supprimer;
  }
  
  public Date getDatenaiss() {
    return datenaiss;
  }
  
  public void setDatenaiss(Date datenaiss) {
    this.datenaiss = datenaiss;
  }
  
  public String getNom() {
    return nom;
  }
  
  public void setNom(String nom) {
    this.nom = nom;
  }
  
  public int getPoids() {
    return poids;
  }
  
  public void setPoids(int poids) {
    this.poids = poids;
  }
  
  public String getPrenom() {
    return prenom;
  }
  
  public void setPrenom(String prenom) {
    this.prenom = prenom;
  }
  
  public float getTaille() {
    return taille;
  }
  
  public void setTaille(float taille) {
    this.taille = taille;
  }
}

Il est très facile de préciser un style particulier pour des lignes paires et impaires.

Il suffit de définir les deux styles désirés.

Exemple dans la partie en-tête de la JSP :
<STYLE type="text/css">
<!--
.titre {
background-color:#000000;
color:#FFFFFF;
}
.paire {
background-color:#EFEFEF;
}
.impaire {
background-color:#CECECE;
}
-->
</STYLE>

Il suffit d'utiliser les attributs headerClass, footerClass, rowClasses ou columnClasses. Avec ces deux derniers attributs, il est possible de préciser plusieurs styles séparés par une virgule pour définir l'apparence de chacune des lignes de façon répétitive.

Exemple :
<h:dataTable value="#{listePersonnes.personneItems}" var="personne" 
  cellspacing="4" width="60%" rowClasses="paire,impaire" headerClass="titre">

Résultat :

Les éléments du tableau peuvent par exemple être sélectionnés grâce à une case à cocher pour permettre de réaliser des traitements sur les éléments marqués.

Il suffit de rajouter dans l'exemple précédent une colonne contenant une case à cocher et, sous le tableau, un bouton qui va réaliser les traitements sur les éléments cochés.

Exemple dans la JSP :
...
<h:form>
  <h1>Test</H1>
  <div align="center">
    <h:dataTable value="#{listePersonnes.personneItems}" var="personne" 
	  cellspacing="4" width="60%" rowClasses="paire,impaire" headerClass="titre">
...
    <h:column>
      <f:facet name="header">
        <h:outputText value="Sélection"/>
      </f:facet>
      <h:selectBooleanCheckbox value="#{personne.supprime}" />
    </h:column>
     
    </h:dataTable>
      
    <p>
      <h:commandButton value="Supprimer les sélectionnés"
        action="#{listePersonnes.supprimer}"/>
    </p>
      
  </div>
</h:form>
...

Il reste alors à ajouter les traitements dans la méthode supprimer() de la classe PersonnesBean qui sera appelée lors d'un clic sur le bouton « Supprimer les sélectionnés ».

Exemple :
public class PersonnesBean {

...

  public String supprimer() {
    Iterator iterator = personneItems.iterator();
    Personne pers=null;
    while (iterator.hasNext()) {
      pers = (Personne) iterator.next();
      System.out.println("nom="+pers.getNom()+" "+pers.isSupprime());
      // ajouter les traitements utiles
    }
    return null;
  }
}

Un clic sur le bouton « Supprimer les sélectionnés » affiche dans la console, la liste des éléments avec l'état de la case à cocher.

Exemple :
nom=Nom1 false
nom=Nom2 true
nom=Nom3 false
nom=Nom4 true
nom=Nom5 false
nom=Nom6 true

 

76.10. La gestion et le stockage des données

Les données sont stockées dans un ou plusieurs JavaBeans qui encapsulent les différentes données des composants.

Ces données possèdent deux représentations :

  • une contenue en interne par le modèle
  • une pour leur présentation dans l'interface graphique (pour la saisie ou l'affichage)

Chaque objet de type Renderer possède une représentation par défaut des données. La transformation d'une représentation en une autre est assurée par des objets de type Converter. JSF fournit en standard plusieurs objets de type Converter mais il est aussi possible de développer ses propres objets.

 

76.11. La conversion des données

JSF propose en standard un mécanisme de conversion des données. Celui-ci repose sur un ensemble de classes dont certaines sont fournies en standard pour des conversions de base. Il est possible de définir ses propres classes de conversion pour répondre à des besoins spécifiques.

Ces conversions sont nécessaires car toutes les données transmises et affichées le sont sous la forme de chaînes de caractères. Cependant, leur exploitation dans les traitements nécessite souvent qu'elles soient stockées dans un autre format pour être exploitées : un exemple flagrant est une donnée de type date.

Toutes les données saisies par l'utilisateur sont envoyées dans la requête http sous la forme de chaînes de caractères. Chacune de ces valeurs est désignée par « request value » dans les spécifications de JSF.

Ces valeurs sont stockées dans leurs composants respectifs dans des champs désignés par « submitted value » dans les spécifications.

Ces valeurs sont éventuellement converties implicitement ou explicitement et sont stockées dans leurs composants respectifs dans des champs désignés par « local value ». Ensuite, ces données sont éventuellement validées.

L'intérêt d'un tel procédé est de s'assurer que les données seront valides avant de pouvoir les utiliser dans les traitements. Si la conversion ou la validation échoue, les traitements du cyle de vie de la page sont arrêtés et la page est réaffichéeen montrant les messages d'erreurs. Sinon la phase de mise à jour des données ( « Update model values » ) du modèle est exécutée.

Les spécifications JSF imposent l'implémentation des convertisseurs suivants : javax.faces.DateTime, javax.faces.Number, javax.faces.Boolean, javax.faces.Byte, javax.faces.Character, javax.faces.Double, javax.faces.Float, javax.faces.Integer, javax.faces.Long, javax.faces.Short, javax.faces.BigDecimal et javax.faces.BigInteger.

JSF effectue une conversion implicite des données lorsque celles-ci correspondent à un type primitif, à BigDecimal ou BigInteger en utilisant les convertisseurs appropriés.

Deux convertisseurs sont proposés en standard pour mettre en oeuvre des conversions qui ne correspondent pas à des types primitifs :

  • le tag convertNumber : utilise le convertisseur javax.faces.Number
  • le tag convertDateTime : utilise le convertisseur javax.faces.DateTime

 

76.11.1. Le tag <convertNumber>

Ce tag permet d'ajouter à un composant un convertisseur de valeur numérique.

Ce tag possède les attributs suivants :

Attributs Rôle
type type de valeur. Les valeurs possibles sont number (par défaut), currency et percent
pattern motif de formatage qui sera utilisé par une instance de java.text.DecimalFormat
maxFractionDigits nombre maximum de chiffres composant la partie décimale
minFractionDigits nombre minimum de chiffres composant la partie décimale
maxIntegerDigits nombre maximum de chiffres composant la partie entière
minIntegerDigits nombre minimum de chiffres composant la partie entière
integerOnly booléen qui précise si uniquement la partie entière est prise en compte (false par défaut)
groupingUsed booléen qui précise si le séparateur de groupe d'unité est utilisé (true par défaut)
locale objet de type java.util.Locale permettant de définir la Locale à utiliser pour les conversions
currencyCode code de la monnaie utilisée pour la conversion
currencySymbol symbole de la monnaie utilisée pour la conversion

Exemple :
<p>valeur1 = <h:outputText value="#{convert.prix}">
<f:convertNumber type="currency"/>
</h:outputText>
</p>

<p>valeur2 = <h:outputText value="#{convert.poids}">
<f:convertNumber type="number"/>
</h:outputText>
</p>

<p>valeur3 = <h:outputText value="#{convert.ratio}">
<f:convertNumber type="percent"/>
</h:outputText>
</p>

<p>valeur4 = <h:outputText value="#{convert.prix}">
<f:convertNumber integerOnly="true" maxIntegerDigits="2"/>
</h:outputText>
</p>

<p>valeur5 = <h:outputText value="#{convert.prix}">
<f:convertNumber pattern="#.##"/>
</h:outputText>
</p>

Le code du bean utilisé dans cet exemple est le suivant :

Exemple :
package com.jmd.test.jsf;

public class Convert {
  private int poids;
  private float prix;
  private float ratio;
  
  public Convert() {
    super();
    this.poids = 12345;
    this.prix = 1234.56f ;
    this.ratio = 0.12f ;
  }
  
  
  public int getPoids() {
    return poids;
  }
  
  public void setPoids(int poids) {
    this.poids = poids;
  }
  
  public float getRatio() {
    return ratio;
  }
  
  public void setRatio(float ratio) {
    this.ratio = ratio;
  }
  
  public float getPrix() {
    return prix;
  }
  
  public void setPrix(float prix) {
    this.prix = prix;
  }
}

 

76.11.2. Le tag <convertDateTime>

Ce tag permet d'ajouter à un composant un convertisseur de valeurs temporelles.

Ce tag possède les attributs suivants :

Attributs Rôle
Type type de valeur. Les valeurs possibles sont date (par défaut), time et both
dateStyle style prédéfini de la date. Les valeurs possibles sont short, medium, long, full ou default
timeStyle style prédéfini de l'heure. Les valeurs possibles sont short, medium, long, full ou default
Pattern motif de formatage qui sera utilisé par une instance de java.text.SimpleDateFormat
Locale objet de type java.util.Locale permettant de définir la locale à utiliser pour les conversions
timeZone objet de type java.util.TimeZone utilisé lors des conversions

Exemple :
<p>Date1 = <h:outputText value="#{convertDate.dateNaiss}">
<f:convertDateTime pattern="MM/yyyy"/>
</h:outputText>
</p>

<p>Date2 = <h:outputText value="#{convertDate.dateNaiss}">
<f:convertDateTime pattern="EEE, dd MMM yyyy"/>
</h:outputText>
</p>

<p>Date3 = <h:outputText value="#{convertDate.dateNaiss}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p>

<p>Date4 = <h:outputText value="#{convertDate.dateNaiss}">
<f:convertDateTime dateStyle="full"/>
</h:outputText>
</p>

Le code du bean utilisé comme source dans cet exemple est le suivant :

Exemple :
package com.jmd.test.jsf;

import java.util.Date;

public class ConvertDate {
  private Date dateNaiss;

  public ConvertDate() {
    super();
    this.dateNaiss = new Date();
  }

  public Date getDateNaiss() {
    return dateNaiss;
  }

  public void setDateNaiss(Date dateNaiss) {
    this.dateNaiss = dateNaiss;
  }
}

Résultat :

 

76.11.3. L'affichage des erreurs de conversions

Les messages d'erreurs issus de ces conversions peuvent être affichés en utilisant les tag <message> ou <messages>.

Par défaut, ils contiennent une description : « Conversion error occured ».

Pour modifier ce message par défaut ou l'internationaliser, il faut définir une clé javax.faces.component.UIInput.CONVERSION dans le fichier properties de définition des chaînes de caractères.

Exemple :
javax.faces.component.UIInput.CONVERSION=La valeur saisie n'est pas correctement formatée.

 

76.11.4. L'écriture de convertisseurs personnalisés

JSF fournit en standard des convertisseurs pour les types primitifs et quelques objets de base. Il peut être nécessaire de développer son propre convertisseur pour des besoins spécifiques.

Pour écrire son propre convertisseur, il faut définir une classe qui implémente l'interface Converter. Cette interface définit deux méthodes :

  • Object getAsObject(FacesContext context, UIComponent component, String newValue) : cette méthode permet de convertir une chaîne de caractères en objet
  • String getAsString(FacesContext context, UIComponent component, Object value) : cette méthode permet de convertir un objet en chaîne de caractères

La méthode getAsObject() doit lever une exception de type ConverterException si une erreur de conversion est détectée dans les traitements.

en construction
La suite de ce chapitre sera développée dans une version future de ce document

 

76.12. La validation des données

JSF propose en standard un mécanisme de validation des données. Celui-ci repose sur un ensemble de classes qui permettent de faire des vérifications standard. Il est possible de définir ses propres classes de validation pour répondre à des besoins spécifiques.

La validation peut se faire de deux façons : au niveau de certains composants ou avec des classes spécialement développées pour des besoins spécifiques. Ces classes sont attachables à un composant et sont réutilisables. Ces validations sont effectuées côté serveur.

Les validators sont enregistrés sur des composants. Ce sont des classes qui utilisent des données pour effectuer des opérations de validation de la valeur des données : contrôle de présence, de type de données, de plage de valeurs, de format, ...

 

76.12.1. Les classes de validation standard

Toutes ces classes implémentent l'interface javax.faces.validator.Validator. JSF propose en standard plusieurs classes pour la validation :

  • deux classes de validation sur une plage de données : LongRangeValidator et DoubleRangeValidator
  • une classe de validation de la taille d'une chaîne de caractères : LengthValidator

Pour faciliter l'utilisation de ces classes, la bibliothèque de tags personnalisés Core propose des tags dédiés à la mise en oeuvre de ces classes :

  • validateDoubleRange : utilise la classe DoubleRangeValidator
  • validateLongRange : utilise la classe LongRangeValidator
  • validateLength : utilise la classe LengthValidator

Ces trois tags possèdent deux attributs nommés minimum et maximum qui permettent de préciser respectivement la valeur de début et de fin selon le Validator utilisé. L'un, l'autre ou les deux attributs peuvent être utilisés.

L'ajout d'une validation sur un contrôle peut se faire de plusieurs manières :

  • ajout d'une ou plusieurs validations directement dans la JSP
  • ajout par programmation d'une validation en utilisant la méthode addValidator().
  • certaines implémentations de composants peuvent contenir des validations implicites.

Pour ajouter une validation à un composant dans la JSP , il suffit d'insérer le tag de validation dans le corps du tag du composant.

Exemple :
<h:inputText id="nombre" converter="#{Integer}" required="true" 
  value="#{saisieDonnees.nombre}">
  <f:validate_longrange minimum="1" maximum="9" />
</h:inputText>

Certaines implémentations de composants peuvent contenir des validations implicites en fonction du contexte. C'est par exemple le cas du composant <inputText> qui, lorsque son attribut required est à true, effectue un contrôle de présence de données saisies.

Exemple :
<h:inputText id="nombre" converter="#{Integer}" required="true" 
  value="#{saisieDonnees.nombre}"/>

Toutes les validations sont faites côté serveur dans la version courante de JSF.

Les messages d'erreurs issus de ces conversions peuvent être affichés en utilisant les tags <message> ou <messages>.

Ils contiennent une description par défaut selon le validator utilisé commençant par « Validation error : ».

Pour modifier ce message par défaut ou l'internationaliser, il faut définir une clé dédiée dans le fichier properties de définition des chaînes de caractères. Les clés définies sont les suivantes :

  • javax.faces.component.UIInput.REQUIRED
  • javax.faces.validator.NOT_IN_RANGE
  • javax.faces.validator.DoubleRangeValidator.MAXIMUM
  • javax.faces.validator.DoubleRangeValidator.TYPE
  • javax.faces.validator.DoubleRangeValidator.MINIMUM
  • javax.faces.validator.LongRangeValidator.MAXIMUM
  • javax.faces.validator.LongRangeValidator.MINIMUM
  • javax.faces.validator.LongRangeValidator.TYPE
  • javax.faces.validator.LengthValidator.MAXIMUM
  • javax.faces.validator.LengthValidator.MINIMUM

 

76.12.2. Contourner la validation

Dans certains cas, il est nécessaire d'empêcher la validation. Par exemple, dans une page de saisie d'informations disposant d'un bouton « Valider » et « Annuler ». La validation doit être opérée lors d'un clic sur le bouton « Valider » mais ne doit pas l'être lors d'un clic sur le bouton « Annuler ».

Pour chaque composant dont l'action doit être exécutée sans validation, il faut mettre l'attribut immediate du composant à true.

Exemple :
<h:commandButton value="Annuler" action="annuler" immediate="true"/>

 

76.12.3. L'écriture de classes de validation personnalisées

JSF fournit en standard des classes de validation de base. Il peut être nécessaire de développer ses propres classes de validation pour des besoins spécifiques.

Pour écrire sa propre classe de validation, il faut définir une classe qui implémente l'interface javax.faces.validator.Validator. Cette interface définit une seule méthode :

  • public void validate(FacesContext context, UIComponent component, Object toValidate) : cette méthode permet de réaliser les traitements de validation

Elle attend en paramètre :

  • un objet de type FacesContext qui permet d'accéder au contexte de l'application jsf
  • un objet de type UICOmponent qui contient une référence sur le composant dont la donnée est à valider
  • un objet de type Object qui encapsule la valeur de la données à valider.

La méthode validate() doit lever une exception de type ValidatorException si une erreur dans les traitements de validation est détectée.

Exemple :
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import com.sun.faces.util.MessageFactory;

public class NumeroDeSerieValidator implements Validator {

  public static final String CLE_MESSAGE_VALIDATION_IMPOSSIBLE = 
    "message.validation.impossible";
  
  public void validate(FacesContext contexte, UIComponent composant,
    Object objet) throws ValidatorException {
    String valeur = null;
    boolean estValide = false;
  
    if ((contexte == null) || (composant == null)) {
      throw new NullPointerException();
    }
    if (!(composant instanceof UIInput)) {
      return;
    }
  
    valeur = objet.toString();
  
    Pattern p = Pattern.compile("[0-9][0-9]-[0-9][0-9][0-9]",Pattern.MULTILINE);
    Matcher m = p.matcher(valeur);
    estValide = m.matches();
  
    if (!estValide) {
      FacesMessage errMsg = MessageFactory.getMessage(contexte,
      CLE_MESSAGE_VALIDATION_IMPOSSIBLE);
      throw new ValidatorException(errMsg);
    }
  
  }
}

Dans l'exemple précédent, la valeur à valider doit respecter une expression régulière de la forme deux chiffres, un tiret et trois chiffres.

Si la validation échoue alors il sera nécessaire d'informer l'utilisateur de la raison de l'échec grâce à un message stocké dans le resourceBundle de l'application.

Exemple :
message.validation.impossible=Le format du numéro de série est erroné

La valeur du message dans le resourceBundle peut être obtenue en utilisant la méthode getMessage() de la classe MessageFactory. Cette méthode attend en paramètres le contexte JSF de l'application et la clé du ressourceBundle à extraire. Elle renvoie un objet de type FacesMessages. Il suffit de fournir cet objet à la nouvelle instance de la classe ValidatorException.

Pour pouvoir utiliser une classe de validation, il faut la déclarer dans le fichier de configuration.

Exemple :
<validator>
  <validator-id>com.jmd.test.jsf.NumeroDeSerie</validator-id>
  <validator-class>com.jmd.test.jsf.NumeroDeSerieValidator</validator-class>
</validator>

Le tag <validator-id> permet de définir un identifiant pour la classe de validation. Le tag <validator-class> permet de préciser la classe pleinement qualifiée.

Pour utiliser la classe de validation dans une page, il faut utiliser le tag <validator> en fournissant à l'attribut validatorId la valeur donnée au tag <validator-id> dans le fichier de configuration :

Exemple :
<h:panelGrid columns="2">
  <h:outputText value="Numéro de série : " />
  <h:panelGroup>
    <h:inputText value="#{validation.numeroSerie}" id="numeroSerie" required="true">
      <f:validator validatorId="com.jmd.test.jsf.NumeroDeSerie"/>
    </h:inputText>
    <h:message for="numeroSerie"/>
  </h:panelGroup>
</h:panelGrid>

La saisie d'un numéro répondant à l'expression régulière et l'appui sur la touche entrée n'affiche aucun message d'erreur :

La saisie d'un numéro ne répondant pas à l'expression régulière affiche le message d'erreur :

 

76.12.4. La validation à l'aide de bean

Il est possible de définir une méthode dans un bean qui va offrir les services de validation. Cette méthode doit avoir une signature similaire à celle de la méthode validate() de l'interface Validator.

Exemple :
package com.jmd.test.jsf; 

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
 
import javax.faces.application.FacesMessage; 
import javax.faces.component.UIComponent; 
import javax.faces.component.UIInput; 
import javax.faces.context.FacesContext; 
import javax.faces.validator.ValidatorException; 
 
import com.sun.faces.util.MessageFactory; 
 
public class Validation { 
  public static final String CLE_MESSAGE_VALIDATION_IMPOSSIBLE = 
    "message.validation.impossible"; 
 
  private String numeroSerie; 
 
  public String getNumeroSerie() { 
    return numeroSerie; 
  } 
 
  public void setNumeroSerie(String numeroSerie) { 
    this.numeroSerie = numeroSerie; 
  } 

  public void valider(FacesContext contexte, UIComponent composant, Object objet) { 
    String valeur = null; 
    boolean estValide = false; 
 
    if ((contexte == null) || (composant == null)) { 
      throw new NullPointerException(); 
    } 
    if (!(composant instanceof UIInput)) { 
      return; 
    } 
 
    valeur = objet.toString(); 
   
    Pattern p = Pattern.compile("[0-9][0-9]-[0-9][0-9][0-9]",Pattern.MULTILINE); 
    Matcher m = p.matcher(valeur); 
    estValide = m.matches(); 
 
    if (!estValide) { 
      FacesMessage errMsg = MessageFactory.getMessage(contexte, 
      CLE_MESSAGE_VALIDATION_IMPOSSIBLE); 
      throw new ValidatorException(errMsg); 
    } 
  } 
}

Pour utiliser cette méthode, il faut utiliser l'attribut validator et lui fournir en paramètre une expression qui désigne la méthode d'une instance du bean.

Exemple :
<h:panelGrid columns="2"> 
  <h:outputText value="Numéro de série : " /> 
  <h:panelGroup> 
    <h:inputText value="#{validation.numeroSerie}" id="numeroSerie" 
      required="true" validator="#{validation.valider}" /> 
    <h:message for="numeroSerie"/> 
  </h:panelGroup> 
</h:panelGrid>

Cette approche est particulièrement utile pour des besoins spécifiques à une application car sa mise en oeuvre est difficilement portable d'une application à une autre.

 

76.12.5. La validation entre plusieurs composants

De base, le modèle de validation des données proposé par JSF repose sur une validation unitaire de chaque composant. Il est cependant fréquent d'avoir besoin de faire une validation en fonction des données d'un ou plusieurs autres composants.

Pour réaliser ce genre de tâche, il faut créer un backing bean qui aura accès à chacun des composants nécessaires aux traitements et définir dans ce bean une méthode qui va réaliser les traitements de validation.

Exemple :
package com.jmd.test.jsf; 
 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
 
import javax.faces.application.FacesMessage; 
import javax.faces.component.UIComponent; 
import javax.faces.component.UIInput; 
import javax.faces.context.FacesContext; 
import javax.faces.validator.ValidatorException; 
 
import com.sun.faces.util.MessageFactory; 
 
public class Validation { 
  public static final String CLE_MESSAGE_VALIDATION_IMPOSSIBLE = 
    "message.validation.impossible"; 
   
  private String numeroSerie; 
  private String cle; 
  private UIInput cleInput; 
  private UIInput numeroSerieInput; 
   
  public String getNumeroSerie() { 
    return numeroSerie; 
  } 
   
  public void setNumeroSerie(String numeroSerie) { 
    this.numeroSerie = numeroSerie; 
  } 
  public void valider(FacesContext contexte, UIComponent composant, Object objet) { 
    String valeur = null; 
    boolean estValide = false; 
   
    if ((contexte == null) || (composant == null)) { 
      throw new NullPointerException(); 
    } 
    if (!(composant instanceof UIInput)) { 
      return; 
    } 
    
    valeur = objet.toString(); 
    
    Pattern p = Pattern.compile("[0-9][0-9]-[0-9][0-9][0-9]",Pattern.MULTILINE); 
    Matcher m = p.matcher(valeur); 
    estValide = m.matches(); 
    
    if (!estValide) { 
      FacesMessage errMsg = MessageFactory.getMessage(contexte, 
      CLE_MESSAGE_VALIDATION_IMPOSSIBLE); 
      throw new ValidatorException(errMsg); 
    } 
  } 
  
  public void validerCle(FacesContext contexte, UIComponent composant, Object objet) { 
    System.out.println("validerCle"); 
  
    String valeurNumero = numeroSerieInput.getLocalValue().toString(); 
    String valeurCle = cleInput.getLocalValue().toString(); 
    boolean estValide = false; 
    if (contexte == null) { 
      throw new NullPointerException(); 
    } 
  
    Pattern p = Pattern.compile("[0-9][0-9]-[0-9][0-9][0-9]",Pattern.MULTILINE); 
    Matcher m = p.matcher(valeurNumero); 
    estValide = m.matches() && valeurCle.equals("789"); 
  
    System.out.println("estValide="+estValide); 
    if (!estValide) { 
      FacesMessage errMsg = MessageFactory.getMessage(contexte, 
      CLE_MESSAGE_VALIDATION_IMPOSSIBLE); 
      throw new ValidatorException(errMsg); 
    } 
  } 
  
  public String getCle() { 
    return cle; 
  } 
  
  public void setCle(String cle) { 
    this.cle = cle; 
  } 
  
  public UIInput getCleInput() { 
    return cleInput; 
  } 
  
  public void setCleInput(UIInput cleInput) { 
    this.cleInput = cleInput; 
  } 
  
  public UIInput getNumeroSerieInput() { 
    return numeroSerieInput; 
  } 
  
  public void setNumeroSerieInput(UIInput numeroSerieInput) { 
    this.numeroSerieInput = numeroSerieInput; 
  } 
}

Il suffit alors d'ajouter un champ caché dans la vue sur lequel la classe de validation sera appliquée.

Exemple :
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 
<%@ page language="java" %> 
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> 
<html> 
<f:view> 
<head> 
  <title>Tests de validation</title> 
</head> 
<body bgcolor="#FFFFFF"> 
  <h:form> 
  <h2>Tests de validation</h2> 
   
  <h:panelGrid columns="2"> 
    <h:outputText value="Numéro de série : " /> 
    <h:panelGroup> 
      <h:inputText value="#{validation.numeroSerie}" id="numeroSerie" 
        required="true" binding="#{validation.numeroSerieInput}" /> 
      <h:message for="numeroSerie"/> 
    </h:panelGroup> 
    <h:outputText value="clé : " /> 
    <h:panelGroup> 
      <h:inputText value="#{validation.cle}" id="cle" binding="#{validation.cleInput}" 
        required="true" /> 
      <h:message for="validationCle"/> 
    </h:panelGroup> 
  </h:panelGrid> 
   
  <h:inputHidden id="validationCle" validator="#{validation.validerCle}" value="nul"/> 
   
  <h:commandButton value="Valider" action="submit"/> 
   
  </h:form> 
</body> 
</f:view> 
</html>

 

76.12.6. L'écriture de tags pour un convertisseur ou un validateur de données

L'écriture d'un tag personnalisé facilite l'utilisation d'un convertisseur ou d'un validateur et permet de lui fournir des paramètres.

Il faut définir une classe nommée handler qui va contenir les traitements du tag. Cette classe doit hériter d'une sous-classe dédiée selon le type d'élément que va représenter le tag :

  • ConverterTag : si le tag concerne un convertisseur
  • ValidatorTag : si le tag concerne un validateur
  • UIComponentTag et UIComponentBodyTag : si le tag concerne un composant

Le handler est un bean dont les propriétés doivent correspondre à chaque attribut défini dans le tag.

Pour pouvoir utiliser un tag personnalisé, il faut définir un fichier .tld.

Ce fichier au format XML défini dans les spécifications des JSP permet de fournir des informations sur la bibliothèque de tags personnalisés notamment la version des spécifications utilisées et des informations sur chaque tag.

Enfin, il est nécessaire de déclarer l'utilisation de la bibliothèque de tags personnalisés dans la JSP.

 

76.12.6.1. L'écriture d'un tag personnalisé pour un convertisseur

Il faut définir un handler pour le tag qui est un bean héritant de la classe ConverterTag.

Il est important dans le constructeur du handler de faire un appel à la méthode setConverterId() en lui passant un id défini dans le fichier de configuration de l'application JSF.

Il faut redéfinir la méthode release() dont les traitements vont permettre de réinitialiser les propriétés de la classe. Ceci est important lorsque, pour améliorer les performances, on souhaite placer ces objets dans un pool. La méthode release() est dans ce cas utilisée pour recycler les instances du pool non utilisées.

Il faut ensuite redéfinir la méthode createConverter() qui va permettre la création d'une instance du converter en utilisant les éventuels valeurs des attributs du tag.

La valeur fournie à un attribut d'un tag pour être soit un littéral soit une expression dont le contenu devra être évalué au moment de son utilisation.

 

en construction
La suite de ce chapitre sera développée dans une version future de ce document

 

76.12.6.2. L'écriture d'un tag personnalisé pour un validateur

L'écriture d'un tag personnalisé pour un validateur suit les mêmes règles que pour un convertisseur. La grande différence est que la classe handler doit hériter de la classe ValidatorTag. La méthode à appeler dans le constructeur est la méthode setValidatorId() et la méthode à redéfinir pour créer une instance du validateur est la méthode createValidator().

 

en construction
La suite de ce chapitre sera développée dans une version future de ce document

 

76.13. La sauvegarde et la restauration de l'état

JSF sauvegarde l'état de chaque élément présent dans la vue : les composants, les convertisseurs, les validateurs, ... pourvu que ceux-ci mettent en oeuvre un mécanisme adéquat.

Ces états sont stockés dans un champ de type hidden dans la vue pour permettre leur échange entre deux requêtes si l'application le prévoit dans le fichier de configuration.

Ce mécanisme peut prendre deux formes selon que :

  • la classe qui encapsule l'élément implémente l'interface Serializable
  • la classe qui encapsule l'élément implémente l'interface StateHolder

Dans le premier cas, c'est le mécanisme standard de la sérialisation qui sera utilisé. Il nécessite donc très peu voire aucun code particulier si les champs de la classe sont tous d'un type qui est sérialisable.

L'implémentation de l'interface StateHolder nécessite la définition des deux méthodes définies dans l'interface (saveState() et restoreState()) et la présence d'un constructeur par défaut. Cette approche peut être intéressante pour obtenir un contrôle très fin de la sauvegarde et de la restauration de l'état.

La méthode saveState(FacesContext) renvoie un objet sérialisable qui va contenir les données de l'état à sauvegarder. La méthode restoreState(FacesContext, Object) effectue l'opération inverse.

Il est aussi nécessaire de définir une propriété nommée transient de type booléen qui précise si l'état doit être sauvegardé ou non.

Si l'élément n'implémente pas l'interface Serializable ou StateHolder alors son état n'est pas sauvegardé entre deux échanges de la vue.

 

76.14. Le système de navigation

Une application de type web se compose d'un ensemble de pages dans lequel l'utilisateur navigue en fonction de ses actions.

Un système de navigation standard peut être facilement mis en oeuvre avec JSF grâce à un paramétrage au format XML dans le fichier de configuration de l'application.

Le système de navigation assure la gestion de l'enchaînement des pages en utilisant des actions. Les règles de navigation sont des chaînes de caractères qui sont associées à une page d'origine et qui permettent de déterminer la page de résultat. Toutes ces règles sont contenues dans le fichier de configuration face-config.xml.

La déclaration de ce système de navigation ressemble à celle utilisée dans le framework Struts.

Le système de navigation peut être statique ou dynamique. Dans ce dernier cas, des traitements particuliers doivent être mis en place pour déterminer la cible de la navigation.

Exemple :
...
  <navigation-rule> 
    <from-view-id>/login.jsp</from-view-id> 
    <navigation-case> 
      <from-outcome>login</from-outcome> 
      <to-view-id>/accueil.jsp</to-view-id> 
    </navigation-case> 
  </navigation-rule>
...

La tag <navigation-rule> permet de préciser des règles de navigation.

La tag <from-view-id> permet de préciser la page concernée. Ce tag n'est pas obligatoire : sans sa présence, il est possible de définir une règle de navigation applicable à toutes les pages JSF de l'application.

Exemple :
<navigation-rule> 
  <navigation-case> 
    <from-outcome>logout</from-outcome> 
    <to-view-id>/logout.jsp</to-view-id> 
  </navigation-case> 
</navigation-rule>

Il est aussi possible de désigner un ensemble de pages dans le tag <from-view-id> en utilisant le caractère * dans la valeur du tag. Ce caractère * ne peut être utilisé qu'une seule fois dans la valeur du tag et il doit être en dernière position.

Exemple :
<from-view-id>/admin/*</from-view-id>

Le tag <navigation-case> permet de définir les différents cas.

La valeur du tag <from-outcome> doit correspondre au nom d'une action.

Le tag <to-view-id> permet de préciser la page qui sera affichée. L'URL fournie comme valeur doit commencer par un slash et doit préciser une page possédant une extension brute (ne surtout pas mettre une URL utilisée par la servlet faisant office de contrôleur).

Le tag <redirect/> inséré juste après le tag <to-view-id> permet au navigateur de l'utilisateur d'effectuer la redirection vers la page indiquée.

La gestion de la navigation est assurée par une instance de la classe NavigationHandler, gérée au niveau de l'application. Ce gestionnaire utilise la valeur d'un attribut action d'un composant pour déterminer la page suivante et faire la redirection vers la page adéquate en fonction des informations fournies dans le fichier de configuration.

La valeur de l'attribut action peut être statique : dans ce cas la valeur est en dur dans le code de la vue

Exemple :
<h:commandButton action="login"/>

La valeur de l'attribut action peut être dynamique : dans ce cas la valeur est déterminée par l'appel d'une méthode d'un bean

Exemple :
<h:commandButton action="#{login.verifierMotDePasse}"/>

Dans ce cas, la méthode appelée ne doit pas avoir de paramètre et doit retourner une chaîne de caractères définie dans le fichier de configuration.

Lors des traitements par le NavigationHandler, si aucune action ne trouve de correspondance dans le fichier de configuration pour la page alors la page est simplement réaffichée.

 

76.15. La gestion des événements

Le modèle de gestion des événements de JSF est similaire à celui utilisé dans les JavaBeans : il repose sur les Listener et les Event pour traiter les événements générés dans les composants graphiques suite aux actions de l'utilisateur.

Un objet de type Event encapsule le composant à l'origine de l'événement et des données relatives à cet événement.

Pour être notifié d'un événement particulier, il est nécessaire d'enregistrer un objet qui implémente l'interface Listener auprès du composant concerné.

Lors de certaines actions de l'utilisateur, un événement est émis.

L'implémentation JSF propose deux types d'événements :

  • Value changed : ces événements sont émis lors du changement de la valeur d'un composant de type UIInput, UISelectOne, UISelectMany, et UISelectBoolean
  • Action : ces événements sont émis lors d'un clic sur un hyperlien ou un bouton qui sont des composants de type UICommand

JSF propose de transposer le modèle de gestion des événements des interfaces graphiques des applications standalone aux applications de type web utilisant JSF.

La gestion des événements repose donc sur deux types d'objets

  • Event : classe qui encapsule l'événement lui-même
  • Listener : classe qui va encapsuler les traitements à réaliser pour un type d'événement

Comme pour les interfaces graphiques des applications standalone, la classe de type Listener doit s'enregistrer auprès du composant concerné. Lorsque celui-ci émet un événement suite à une action de l'utilisateur, il appelle le Listener enregistré en lui fournissant en paramètre un objet de type Event.

Exemple :
<h:selectOneMenu ... valueChangeListener="#{choixLangue.langueChangement}">
  ...
</h:selectOneMenu>

JSF supporte trois types d'événements :

  • les changements de valeurs : concernent les composants qui permettent la saisie ou la sélection d'une valeur lorsque cette valeur change
  • les actions : concernent un clic sur un bouton (commandButton) ou un lien (commandLink)
  • les événements liés au cycle de vie : ils sont émis par le framework JSF durant le cyle de vie des traitements

Les traitements des listeners peuvent affecter la suite du cycle de vie de plusieurs manières :

  • par défaut, laisser les traitements se poursuivre
  • demander l'exécution immédiate de la dernière étape en utilisant la méthode FacesContext.renderResponse()
  • arrêter les traitements du cyle de vie en utilisant la méthode FacesContext.responseComplete()

 

76.15.1. Les événements liés à des changements de valeur

Il y a deux façons de préciser un listener de type valueChangeListener sur un composant :

  • utiliser l'attribut valueChangeListener
  • utiliser le tag valueChangeListener

L'attribut valueChangeListener permet de préciser, par une expression, la méthode exécutée durant les traitements du cyle de vie de la requête. Pour que ces traitements puissent être déclenchés, il faut soumettre la page.

Exemple :
<h:selectOneMenu value="#{choixLangue.langue}" onchange="submit()" 
  valueChangeListener="#{choixLangue.langueChangement}">
  <f:selectItems value="#{choixLangue.langues}"/>
</h:selectOneMenu>

La méthode ne renvoie aucune valeur et attend en paramètre un objet de type ValueChangeEvent.

Exemple :
package com.jmd.test.jsf;

import java.util.Locale;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

public class ChoixLangue {
  private static final String LANGUE_FR = "Français";
  private static final String LANGUE_EN = "Anglais";
  private String langue = LANGUE_FR;
  
  private SelectItem[] langueItems = {
    new SelectItem(LANGUE_FR, "Français"),
    new SelectItem(LANGUE_EN, "Anglais") };
  
  public SelectItem[] getLangues() {
    return langueItems;
  }
 
  public String getLangue() {
    return langue;
  }
  
  public void setLangue(String langue) {
    this.langue = langue;
  }
  
  public void langueChangement(ValueChangeEvent event) {
    FacesContext context = FacesContext.getCurrentInstance();
    System.out.println("Changement de la langue : "+event.getNewValue());
    if (LANGUE_FR.equals((String) event.getNewValue()))
      context.getViewRoot().setLocale(Locale.FRENCH);
    else
      context.getViewRoot().setLocale(Locale.ENGLISH);
    }
  }
}

La classe ValueChangeEvent possède plusieurs méthodes utiles :

Méthode Rôle
UIComponent getComponent() renvoie le composant qui a généré l'événement
Object getNewValue() renvoie la nouvelle valeur (convertie et validée)
Object getOldValue() renvoie la valeur précédente

Le tag valueChangeListener permet aussi de préciser un listener. Son attribut type permet de préciser une classe implémentant l'interface ValueChangeListener.

Exemple :
<h:selectOneMenu value="#{choixLangue.langue}" onchange="submit()">
  <f:valueChangeListener type="com.jmd.test.jsf.ChoixLangueListener"/>
  <f:selectItems value="#{choixLangue.langues}"/>
</h:selectOneMenu>

Une telle classe doit définir une méthode processValueChange() qui va contenir les traitements exécutés en réponse à l'événement.

Exemple :
package com.jmd.test.jsf; 

import java.util.Locale; 

import javax.faces.context.FacesContext; 
import javax.faces.event.AbortProcessingException; 
import javax.faces.event.ValueChangeEvent; 
import javax.faces.event.ValueChangeListener; 

public class ChoixLangueListener implements ValueChangeListener { 

  private static final String LANGUE_FR = "Français"; 

  private static final String LANGUE_EN = "Anglais"; 

  public void processValueChange(ValueChangeEvent event) 
    throws AbortProcessingException { 
    FacesContext context = FacesContext.getCurrentInstance(); 
    System.out.println("Changement de la langue : " + event.getNewValue()); 
    if (LANGUE_FR.equals((String) event.getNewValue())) 
      context.getViewRoot().setLocale(Locale.FRENCH); 
    else 
      context.getViewRoot().setLocale(Locale.ENGLISH); 
    } 
  }
}

 

76.15.2. Les événements liés à des actions

Les actions sont des clics sur des boutons ou des liens. Le clic sur un composant de type commandLink ou commandButton déclenche automatiquement la soumission de la page.

Il y a deux façons de préciser un listener de type actionListener sur un composant :

  • utiliser l'attribut actionListener
  • utiliser le tag actionListener

L'attribut actionListener permet de préciser, par une expression, la méthode exécutée durant les traitements du cyle de vie de la requête.

Exemple :
<table align="center" width="50%">
  <tr>
    <td width="50%"><h:commandButton image="images/bouton_valider.gif"
      actionListener="#{saisieDonnees.traiterAction}"
      id="Valider" />
    </td>
    <td><h:commandButton image="images/bouton_annuler.gif"
      actionListener="#{saisieDonnees.traiterAction}"
      id="Annuler"/>
    </td>
  </tr>
</table>

Cette méthode attend en paramètre un objet de type ActionEvent.

Exemple :
package com.jmd.test.jsf;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

public class SaisieDonnees {

  public void traiterAction(ActionEvent e) {

    FacesContext context = FacesContext.getCurrentInstance();

    String clientId = e.getComponent().getClientId(context);
    System.out.println("traiterAction : clientId=" + clientId);
    
  }
}

Le tag valueChangeListener permet aussi de préciser un listener. Son attribut type permet de préciser une classe implémentant l'interface ValueChangeListener.

Exemple :
<table align="center" width="50%">
  <tr>
    <td width="50%"><h:commandButton image="images/bouton_valider.gif" id="Valider" >
        <f:actionListener type="com.jmd.test.jsf.SaisieDonneesListener"/>
      </h:commandButton>
      </td>
    <td><h:commandButton image="images/bouton_annuler.gif" id="Annuler">
        <f:actionListener type="com.jmd.test.jsf.SaisieDonneesListener"/>
      </h:commandButton>
    </td>
  </tr>
</table>

Une telle classe doit définir la méthode processAction() définie dans l'interface.

Exemple :
package com.jmd.test.jsf;

import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

public class SaisieDonneesListener implements ActionListener {

  public void processAction(ActionEvent e) throws AbortProcessingException {
    FacesContext context = FacesContext.getCurrentInstance();

    String clientId = e.getComponent().getClientId(context);
    System.out.println("processAction : clientId=" + clientId);

  }
}

 

76.15.3. L'attribut immediate

L'attribut immediate permet de demander les traitements immédiats des listeners.

Par exemple, sur une page un composant possède un attribut required et un second possède un listener. Les traitements du second doivent pouvoir être réalisés sans que le premier composant n'affiche un message d'erreur lié à sa validation.

Le cycle de traitement de la requête est modifié lorsque l'attribut immediate est positionné dans un composant. Dans ce cas, les données du composant sont converties et validées si nécessaire puis les traitements du listener sont exécutés à la place de l'étape « Process validations » (juste après l'étape Apply Request Value).

Exemple :
<h:selectOneMenu value="#{choixLangue.langue}" onchange="submit()" immediate="true">
  <f:valueChangeListener type="com.jmd.test.jsf.ChoixLangueListener"/>
  <f:selectItems value="#{choixLangue.langues}"/>
</h:selectOneMenu>

Par défaut, ceci modifie l'ordre d'exécution des traitements du cycle de vie mais n'empêche pour les traitements prévus de s'exécuter. Pour les inhiber, il est nécessaire de demande au framework JSF d'interrompre les traitements du cycle de vie en utilisant la méthode renderResponse() du FaceCcontext.

Exemple :
  public void langueChangement(ValueChangeEvent event) {
    FacesContext context = FacesContext.getCurrentInstance();
    System.out.println("Changement de la langue : " + event.getNewValue());
    if (LANGUE_FR.equals((String) event.getNewValue())) {
      context.getViewRoot().setLocale(Locale.FRENCH);
    } else {
      context.getViewRoot().setLocale(Locale.ENGLISH);
    }
    context.renderResponse();
  }

Le mode de fonctionnement est le même avec les actionListener hormis le fait que l'appel à la méthode renderResponse() est inutile puisqu'il est automatiquement fait par le framework.

 

76.15.4. Les événements liés au cycle de vie

Le framework émet des événements avant et après chaque étape du cycle de vie des requêtes. Ils sont traités par des phaseListeners.

L'enregistrement d'un phaseListener se fait dans le fichier de configuration dans un tag fils <phase-listener> fils du tag <lifecycle> qui doit contenir le nom pleinement qualifié d'une classe.

Exemple :
<faces-config>
...

  <lifecycle>
    <phase-listener>com.jmd.test.jsf.PhasesEcouteur</phase-listener>
  </lifecycle>

</faces-config>

La classe précisée doit implémenter l'interface javax.faces.event.PhaseListener qui définit trois méthodes :

  • getPhaseId() : renvoie un objet de type PhaseId qui permet de préciser à quelle phase ce listener correspond
  • beforePhase() : traitements à exécuter avant l'exécution de la phase
  • afterPhase() : traitements à exécuter après l'exécution de la phase

La classe PhaseId définit des constantes permettant d'identifier chacune des phases : PhaseId.RESTORE_VIEW, PhaseId.APPLY_REQUEST_VALUES, PhaseId.PROCESS_VALIDATIONS, PhaseId.UPDATE_MODEL_VALUES, PhaseId.INVOKE_APPLICATION et PhaseId.RENDER_RESPONSE

Elle définit aussi la constante PhaseId.ANY_PHASE qui permet de demander l'application du listener à toutes les phases. Cela peut être très utile lors du débogage.

Exemple :
package com.jmd.test.jsf;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class PhasesEcouteur implements PhaseListener {

  public void afterPhase(PhaseEvent pe) {
    System.out.println("Apres " + pe.getPhaseId());
  }

  public void beforePhase(PhaseEvent pe) {
    System.out.println("Avant " + pe.getPhaseId());
  }

  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }
}

Lors de l'appel de la première page de l'application, les informations suivantes sont affichées dans la sortie standard :

Avant RESTORE_VIEW 1
Apres RESTORE_VIEW 1
Avant RENDER_RESPONSE 6
Apres RENDER_RESPONSE 6

Lors d'une soumission de cette page avec une erreur de validation des données, les informations suivantes sont affichées dans la sortie standard :

Avant RESTORE_VIEW 1
Apres RESTORE_VIEW 1
Avant APPLY_REQUEST_VALUES 2
Apres APPLY_REQUEST_VALUES 2
Avant PROCESS_VALIDATIONS 3
Apres PROCESS_VALIDATIONS 3
Avant RENDER_RESPONSE 6
Apres RENDER_RESPONSE 6

Lors d'une soumission de cette page sans erreur de validation des données, les informations suivantes sont affichées dans la sortie standard :

Avant RESTORE_VIEW 1
Apres RESTORE_VIEW 1
Avant APPLY_REQUEST_VALUES 2
Apres APPLY_REQUEST_VALUES 2
Avant PROCESS_VALIDATIONS 3
Apres PROCESS_VALIDATIONS 3
Avant UPDATE_MODEL_VALUES 4
Apres UPDATE_MODEL_VALUES 4
Avant INVOKE_APPLICATION 5
Apres INVOKE_APPLICATION 5
Avant RENDER_RESPONSE 6
Apres RENDER_RESPONSE 6

 

76.16. Le déploiement d'une application

Une application utilisant JSF s'exécute dans un serveur d'applications contenant un conteneur web implémentant les spécifications servlet 1.3 et JSP 1.2 minimum. Une telle application doit être packagée dans un fichier .war.

La compilation des différentes classes de l'application nécessite l'ajout dans le classpath de la bibliothèque servlet.

Elle nécessite aussi l'ajout dans le classpath de la bibliothèque jsf-api.jar de la ou des bibliothèques requises par l'implémentation JSF utilisée.

Ces bibliothèques doivent aussi être disponibles pour le conteneur web qui va exécuter l'application. Le plus simple est de mettre ces fichiers dans le répertoire WEB-INF/lib.

 

76.17. Un exemple d'application simple

Cette section va développer une petite application constituée de deux pages. La première va demander le nom de l'utilisateur et la seconde afficher un message de bienvenue.

Il faut créer un répertoire, par exemple nommé Test_JSF et créer à l'intérieur la structure de l'application qui correspond à la structure de toute application Web selon les spécifications J2EE, notamment le répertoire WEB-INF avec ses sous-répertoires lib et classes.

Il faut ensuite copier les fichiers nécessaires à une utilisation de JSF dans l'application web.

Il suffit de copier les fichiers *.jar du répertoire lib de l'implémentation de référence vers le répertoire WEB-INF/lib du projet.

Il faut créer un fichier à la racine du projet et le nommer index.php

Exemple :
<html>
  <head>
    <meta http-equiv="Refresh" content= "0; URL=login.faces"/>
    <title>Demarrage de l'application</title>
  </head>
  <body>
    <p>D&eacute;marrage de l'application ...</p>
  </body>
</html>

Il faut créer un fichier à la racine du projet et le nommer login.jsp :

Exemple :
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<head>
  <title>Application de tests avec JSF</title>
</head>
<body>
  <h:form>
    <h3>Identification</h3>
    <table>
      <tr>
        <td>Nom : </td>
        <td><h:inputText value="#{login.nom}"/></td>
      </tr>
      <tr>
        <td>Mot de passe :</td>
        <td><h:inputSecret value="#{login.mdp}"/></td>
      </tr>
      <tr>
        <td colspan="2"><h:commandButton value="Login" action="login"/></td>
      </tr>
    </table>
  </h:form>
</body>
</f:view>
</html>

Il faut créer une nouvelle classe nommée com.jmd.test.jsf.LoginBean et la compiler dans le répertoire WEB-INF/classes.

Exemple :
package com.jmd.test.jsf;

public class LoginBean {

  private String nom;
  private String mdp;

  public String getMdp() {
    return mdp;
  }

  public String getNom() {
    return nom;
  }

  public void setMdp(String string) {
    mdp = string;
  }

  public void setNom(String string) {
    nom = string;
  }
}

Il faut créer un fichier à la racine du projet et le nommer accueil.jsp : cette page contiendra la page d'accueil de l'application.

Il faut créer un fichier dans le répertoire /WEB-INF et le nommer faces-config.xml :

Exemple :
<?xml version="1.0"?>

<!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>
  <navigation-rule>
    <from-view-id>/login.jsp</from-view-id>
    <navigation-case>
      <from-outcome>login</from-outcome>
      <to-view-id>/accueil.jsp</to-view-id>
    </navigation-case>
  </navigation-rule>

  <managed-bean>
    <managed-bean-name>login</managed-bean-name>
    <managed-bean-class>com.jmd.test.jsf.LoginBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

</faces-config>

Il faut créer un fichier dans le répertoire /WEB-INF et le nommer web.xml :

Exemple :
<?xml version="1.0"?>

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.php</welcome-file>
  </welcome-file-list>

</web-app>

Il suffit alors de démarrer Tomcat, puis d'ouvrir un navigateur et taper l'URL http://localhost:8089/test_JSF/ (en remplaçant le port 8089 par celui défini dans Tomcat).

Une fois l'application démarrée, la page de login s'affiche

Il faut saisir un nom par exemple test et cliquer sur le bouton « Login ».

Cette exemple ne met en aucune façon en valeur la puissance de JSF mais permet simplement de mettre en place les éléments minimum pour une application l'utilisant.

 

76.18. L'internationalisation

JSF propose des fonctionnalités qui facilitent l'internationalisation d'une application.

Il faut définir un fichier au format properties qui va contenir la définition des chaînes de caractères. Un tel fichier possède les caractéristiques suivantes :

  • le fichier doit avoir l'extension .properties
  • il doit être dans le classpath de l'application
  • il est composé d'une paire clé=valeur par ligne. La clé permet d'identifier de façon unique la chaîne de caractères
Exemple : le fichier msg.properties
login_titre=Application de tests avec JSF
login_identification=Identification
login_nom=Nom
login_mdp=Mot de passe
login_Login=Valider

Ce fichier correspond à la langue par défaut. Il est possible de définir d'autres fichiers pour d'autres langues. Ces fichiers doivent avoir le même nom suivi d'un underscore et du code langue défini par le standard ISO 639 avec toujours l'extension .properties.

Exemple :
msg.properties
msg_en.properties
msg_de.properties

Il faut bien sûr remplacer les valeurs de chaque chaîne par leurs traductions correspondantes.

Exemple :
login_titre=Tests of JSF
login_identification=Login
login_nom=Name
login_mdp=Password
login_Login=Login

Les langues disponibles doivent être précisées dans le fichier de configuration.

Exemple :
<faces-config>
...
  <application>
    <locale-config>
      <default-locale>fr</default-locale>
      <supported-locale>en</supported-locale>
    </locale-config>
  </application>
...
</faces-config>

Pour utiliser l'internationalisation dans les vues, il faut utiliser le tag <f:loadBundle> pour charger le fichier .properties nécessaire. Deux attributs de ce tags sont requis :

  • basename : précise la localisation et le nom de base des fichiers .properties. La notation de la localisation est similaire à celle utilisée pour les packages
  • var : précise le nom de la variable qui va contenir les chaînes de caractères

Il ne reste plus qu'à utiliser la variable définie en utilisant la notation avec un point pour la clé de la chaîne dont on souhaite utiliser la valeur.

Exemple :
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<f:loadBundle basename="com.jmd.test.jsf.msg" var="msg"/>

<head>
  <title><h:outputText value="#{msg.login_titre}"/></title>
</head>
<body>
  <h:form>
    <h3><h:outputText value="#{msg.login_identification}"/></h3>
    <table>
      <tr>
        <td><h:outputText value="#{msg.login_nom}"/> : </td>
        <td><h:inputText value="#{login.nom}"/></td>
      </tr>
      <tr>
        <td><h:outputText value="#{msg.login_mdp}"/> :</td>
        <td><h:inputSecret value="#{login.mdp}"/></td>
      </tr>
      <tr>
        <td colspan="2"><h:commandButton value="#{msg.login_Login}" action="login"/></td>
      </tr>
    </table>
  </h:form>
</body>
</f:view>
</html>

La langue à utiliser est déterminée automatiquement par JSF en fonction des informations contenues dans la propriété Accept-Language de l'en-tête de la requête et du fichier de configuration.

La langue peut aussi être forcée dans l'objet de type view en précisant le code langue dans l'attribut locale.

Exemple :
...
	  <f:view locale="en">
...

Elle peut aussi être déterminée dans le code des traitements. L'exemple suivant va permettre à l'utilisateur de sélectionner la langue utilisée entre Français et Anglais grâce à deux petites icônes cliquables.

Exemple :
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<f:loadBundle basename="com.jmd.test.jsf.Messages" var="msg"/>
<head>
<title>Application de tests avec JSF</title>
</head>
<body>
  <h:form>
  
  <table>
    <tr>
      <td>
        <h:commandLink action="#{langueApp.activerFR}" immediate="true">
          <h:graphicImage value="images/francais.jpg" style="border: 0px"/>
        </h:commandLink>
      </td>
      <td>
        <h:commandLink action="#{langueApp.activerEN}" immediate="true">
          <h:graphicImage value="images/anglais.jpg" style="border: 0px"/>
        </h:commandLink>
      </td>
      <td width="100%">&nbsp;</td>
    </tr>
  </table>
  
  <h3><h:outputText value="#{msg.login_titre}" /></h3>
  <p>&nbsp;</p>
  <h:panelGrid columns="2">
    <h:outputText value="#{msg.login_nom}" />
    <h:panelGroup>
      <h:inputText value="#{login.nom}" id="nom" required="true" 
        binding="#{login.inputTextNom}"/>
      <h:message for="nom"/>
    </h:panelGroup>
    <h:outputText value="#{msg.login_mdp}" />
    <h:inputSecret value="#{login.mdp}"/>
    <h:commandButton value="#{msg.login_valider}" action="login"/>
  </h:panelGrid>
  
  </h:form>
  </body>
</f:view>
</html>

Ce code n'a rien de particulier si ce n'est l'utilisation de l'attribut immediate sur les liens sur le choix de la langue pour empêcher la validation des données lors d'un changement de la langue d'affichage.

Ce sont les deux méthodes du bean qui se chargent de modifier la Locale par défaut du contexte de l'application.

Exemple :
package com.jmd.test.jsf;

import java.util.Locale;

import javax.faces.context.FacesContext;

public class LangueApp {

  public String activerFR() {
    FacesContext context = FacesContext.getCurrentInstance();
    context.getViewRoot().setLocale(Locale.FRENCH);
    return null;
  }

  public String activerEN() {
    FacesContext context = FacesContext.getCurrentInstance();
    context.getViewRoot().setLocale(Locale.ENGLISH);
    return null;
  }
}

Lors de l'exécution, la page s'affiche en français par défaut.

Lors d'un clic sur la petite icône indiquant la langue anglaise, la page est réaffichée en anglais.

 

76.19. Les points faibles de JSF

Malgré ses nombreux points forts, JSF présente aussi quelques points faibles :

  • Maturité de la technologie

    JSF est une technologie récente qui nécessite l'écriture de beaucoup de code. Bien que prévu pour être utilisé avec des utilitaires facilitant la rédaction de la majeure partie de ce code, à l'heure actuelle, seuls quelques outils supportent JSF.


  • Manque de composants évolués en standard

    L'implémentation standard ne propose que des composants simples dont la plupart ont une correspondance directe en HTML. Hormis le composant dataTable aucun composant évolué n'est proposé en standard dans la version 1.0. Il est donc nécessaire de développer ses propres composants ou d'acquérir les composants nécessaires auprès de tiers.


  • Consommation en ressources d'une application JSF

    L'exécution d'une application JSF est assez gourmande en ressource notamment mémoire à cause du mode de fonctionnement du cycle de traitement d'une page. Ce cycle de vie inclut la création en mémoire d'une arborescence des composants utilisés lors des différentes étapes des traitements.


  • Le rendu des composants uniquement en HTML en standard

    Dans l'implémentation de référence le rendu des composants est uniquement possible en HTML alors que JSF intègre un système de rendu (Renderer) découplé des traitements des composants. Pour un rendu différent de HTML, il est nécessaire de développer ses propres Renderers ou d'acquérir un système de rendu auprès de tiers.


 

 

en construction
La suite de ce chapitre sera développée dans une version future de ce document

 


Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]