JavaServer Faces - JSF

Chapitres traités   

JavaServer Faces, une technologie relativement récente dans le monde de J2EE, a été conçue pour simplififier encore plus le développement des applications Web. Il est facile de mettre en oeuvre des pages Web ainsi que les composants Web avec JSF en permettant une connexion adaptée entre ces composants d'interface et la logique métier donnée par les objets distribués. Il permet également d'automatiser le fonctionnement entre les JavaBeans et la page de navigation Web.


Choix du chapitre Introduction

Pourquoi JSF ?

Jusqu'à présent nous avons découvert plusieurs composants Web que sont les servlets et les pages JSP. Ces composants associés à des JavaBeans (ou des EJB) permettent de mettre en oeuvre toutes les applications Web de façon rationnel, surtout si nous respectons l'architecture MVC (Modèle-Vue-Contrôleur). Rappelons que cette architecture nous offre de bien séparer la présentation, la gestion des requêtes ainsi que le traitement issu de la couche métier.

Alors pourquoi cette nouvelle technologie JSF ? Qu'apporte t-elle de plus ?

JSF utilise et rend plus pratique le modèle MVC dont je rappelle l'architecture :



Le contrôleur que nous avions mis en oeuvre devait récupérer l'ensemble des requêtes, et après analyse devait activer le modèle correspondant et pour finir proposer l'affichage en sollicitant la bonne page JSP. Le problème, c'est que le code de la servlet devient vite conséquent et très difficile à lire. Par ailleurs, à chaque fois que vous voulez définir une nouvelle page, il est nécessaire de recompiler la servlet contrôleur.

Avec la technologie JSF bien entendu, le contrôleur existe toujours, mais nous n'y avons plus accès (le contrôleur se nomme FacesServlet). Le contrôleur récupère toujours les requêtes et réagit en conséquence. Toutefois, la navigation et le traitement de la requête est décrit au travers d'un fichier de configuration <faces-config.xml> qu'il est très facile à écrire (et donc à lire). Il se complète au moyen de balises XML spécifiques. Grâce à cette solution, il n'est plus nécessaire de recompiler la servlet contrôleur, et par voie de conséquence, les modifications éventuelles sont faciles à mettre en oeuvre.

JSF est différent parce qu'il a besoin d'un autre composant Web qui lui sert de support. Il ne peut pas exister tout seul.JSF est donc une surcouche qui se place au dessus des autres composants Web en utilisant pleinement toutes leurs compétences. JSF est avant tout un ensemble de bibliothèques de balises personnalisées qui sont capables d'être mise en oeuvre par les pages JSP.



Finalement, la connaissance des autres composants Web comme les JSP et les servlets est requise pour bien maîtriser l'élaboration de JSF.

Nous avons précédemment vu que le couple Page JSP et JavaBeans permettait de séparer la logique métier de la présentation, mais malheureusement pas totalement. En effet, dans la page JSP, vous êtes au moins obligé de déclarer ces JavaBeans avec leurs différentes propriétés afin que la page JSP puisse les utiliser par la suite. Pourtant cette démarche ne correspond spécialement à un affichage, et c'est pourtant ce qu'est censée faire une page JSP. Voici d'ailleurs un exemple de code :

nombre.jspf
<jsp:useBean id="nombre" class="bean.Nombre" scope="session" />
<jsp:setProperty name="nombre" property="valeur" param="valeur" />
<jsp:setProperty name="nombre" property="maxi" value="${initParam.NombreMaxi}" />

<h3>
   <form action="Operation.jsp">
      Nombre d'op&eacute;randes : 
      <select name="valeur">
         ${nombre.options}
      </select>
      <input type="submit" value="Valider ce choix" />
   </form>  
</h3>

Toute la première partie de ce fragment de page sert à déclarer le JavaBean bean.Nombre, la présentation visuelle de la page se fait par la suite.

En utilisant la technologie JSF, la déclaration des JavaBeans ne se fait plus sur la page JSP mais dans le fichier de configuration <faces-config.xml> que nous avons évoqué plus haut. Dans la page JSP qui utilise les compétences de JSF ne se trouve plus que des balises personnalisées correspondant uniquement à la présentation. Ainsi, le traitement de la logique métier est totalement séparée de la partie visuelle.

Voici quelques unes des fonctionnalités de JSF :

  1. Une séparation nette entre la couche de présentation et les autres couches.
  2. Le mapping HTML/Objet.
  3. Un modèle riche de composants graphiques réutilisables.
  4. Une gestion de l'état de l'interface entre les différentes requêtes.
  5. Une liaison simple entre les actions côté client de l'utilisateur et le code Java correspondant côté serveur.
  6. La création de composants personnalisés.
  7. Le support de différents clients (HTML, WML, XML, ...) grâce à la séparation des problématiques de construction de l'interface et du rendu de cette interface.

 

Choix du chapitre Architecture et principe de fonctionnement

JavaServer Faces est un framework de composants GUI serveur conçu pour le développement d'applications Web en langage Java. Il est constitué d'un ensemble de balises spécialement conçues pour exprimer les interfaces JSF à l'intérieur d'une page JSP.

Les composants principaux de la technologie JSF sont les suivants :

  1. Une API pour représenter les composants d'interface utilisateur et gérer leur état, intercepter les événements, effectuer les validations de saisie côté serveur et des conversions de données, définir la navigation entre les pages, gérer l'internationnalisation, etc.
  2. Une bibliothèque de balises JSP pour construire des composants graphiques au sein d'une page JSP.

Le modèle de programmation JSF permet, à moindre effort, d'effectuer les tâches suivantes :

  1. Lier les événements client générés au code serveur.
  2. Lier les composants d'interface utilisateur de la page aux données du serveur.
  3. Construire une interface graphique à partir de composants réutilisables.
  4. Sauvegarder et restaurer l'état de l'interface graphique tout au long de la durée de vie des requêtes serveur.

L'interface graphique créée avec la technologie JSF s'exécute sur le serveur et présente le résultat de la génération sur le poste client.
.

En réalité, la page JSP inclut des balises spécifiques supportant la technologie JSF et délègue la gestion de la présentation à une structure interne qui gére les objets référencés par la page JSP sur le serveur Web.

Ces objets sont les suivants :

  1. Composants GUI, qui mappent les balises des pages JSP.
  2. Gestionnaire d'événements, validateurs et convertisseurs de format, qui sont enregistrés sur les composants. Les validateurs (validators), qui sont fournis en standard dans les implémentation JSF, sont des objets qui permettent de valider les champs de saisie, comme la longueur d'un libellé ou le type de donnée. Les convertisseurs (converters) permettent de transformer les données avant de les transmettre aux composants dans la page. Plusieurs converters sont fournis en standard. Vous pouvez les utiliser ou en créer de nouveaux, comme les validators.
  3. Objets qui encapsulent les données et les traitements spécifiques des composants.

 

Choix du chapitre Notre première page JSF - Message de bienvenue

Après toute cette petite théorie, nous allons mettre en oeuvre notre première page JSF. Elle est volontairement modeste et de peu d'utilité. Elle nous permettra, par contre, de découvrir toute l'ossature nécessaire à l'élaboration d'une page JSF. Cette page va tout simplement afficher un message de bienvenue (Bien entendu, nous aurions pu nous passer de JSF pour cela).

Modèle MVC

Comme nous l'avons l'ossature de JSF respecte l'architecture Modèle-Vue-Contrôleur. Par contre, pour cet exemple, le Modèle n'existe pas. Pour toutes les applications Web qui utilise la technologie JSF, le contrôle est toujours assurée par la servlet FacesServlet. Cette servlet n'est pas directement accessible. Tous les réglages nécessaires à la bonne gestion de cette servlet se fait au travers du fichier de configuration <faces-config.xml>.

Par ailleurs, la technologie JSF ne peut exister seule puisque, comme nous l'avons vu, c'est une surcouche qui se place sur d'autres composants Web. Nous devons donc prendre une page JSP qui servira de support, ici <Bienvenue.jsp>.

Constitution de l'application Web

Notre application Web comporte :

  1. La page d'accueil : Bienvenue.jsp
  2. Les deux fichiers de configurations : web.xml (descripteur de déploiement) et faces-config.xml (Contrôle de navigation entre les pages Web et mise en place de la communication entre ces pages Web et les JavaBeans qui assurent le traitement de la couche métier).
  3. Ensemble des bibliothèques nécessaires au bon fonctionnement de la technologie JSF : deux sont indispensables jsf-api.jar et jsf-impl.jar. Il est souvent nécessaire de posséder en plus les librairies standard de la JSTL qui se nomment jstl.jar et standard.jar. Enfin quelques librairies optionnelles peuvent s'avérer intéressantes, elles commencent alors par commons-***.jar.

Descripteur de déploiement <web.xml>

Notre descripteur de déploiement doit activer le contrôleur FacesServlet qui se trouve dans le paquetage javax.faces.webapp. Ce contrôleur doit systématiquement être activé quelque soit les requêtes venant du client. Il faut donc que l'URL de connexion à cette servlet utilise le jocker (*). Par contre, pour que ce contrôleur soit opérationnel par rapport à la technologie JSF, l'URL doit être systématiquement préfixé de /faces/.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app 
      version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
         
    <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>
            faces/Bienvenue.jsp
        </welcome-file>
    </welcome-file-list>
</web-app>

Vu ces considérations, pour accéder à la page d'accueil, nous devons donc écrire au niveau de la balise <welcome-file> faces/Bienvenue.jsp.
.

Fichier de configuration <faces-config.xml>

Ce fichier permet de spécifier la navigation que vous désirez suivre entre les différentes pages de votre application Web. Par ailleurs, ce fichier permet de mettre en relation les pages JSP avec les composants JavaBeans correspondant à la logique métier. C'est à l'aide de ce fichier que les JavaBeans sont créés. Ce n'est plus la page JSP qui le fait. Ainsi, nous avons bien une séparation nette entre l'affichage de l'information d'une part, et le traitement de l'information d'autre part.

faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>


<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
    
  
</faces-config>

L'élément racine de ce fichier est <faces-config>. Nous remarquons que cet élément racine ne comporte aucun autre élément. Effectivement, une seule page est affichée, il n'y a donc pas de navigation possible. Par ailleurs, nous n'avons pas de logique métier, donc pas de JavaBean à mettre en oeuvre. Notre page propose juste un affichage basique sans calcul intermédiaire.

Page d'accueil de l'application Web <Bienvenue.jsp>

Comme je l'ai déjà fait remarquer plus haut, malgré l'utilisation de JSF, nous sommes obligé de prendre un composant Web qui sert de support. Comme il sagit d'une présentation visuelle, il va de soit qu'il faut prendre alors une page JSP.

Bienvenue.jsp
 1 <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
 2 <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
 3 
 4 <f:view>
 5    <html>
 6        <body>
 7           <h1><h:outputText value="Bienvenue à tout le monde" /></h1>
 8       </body>
 9    </html>
10 </f:view>
11

JSF utilise en fait un ensemble de balises personnalisées de hauts niveaux. Je rappelle que dans ce cas là, il est nécessaire d'importer les bibliothèques correspondantes afin de pouvoir les utiliser. C'est ce que réalisent les deux premières lignes.

Souvent, le préfixe de la biblothèque core de JSF est f plutôt que c. En effet, ce dernier préfixe est souvent utilisé par la JSTL standard.
.

Les deux biblothèques de balises sont spécialisées et permettent de gérer deux situations que nous trouvons fréquemment dans les pages Web dynamiques.

  1. La bibliothèque html qui s'interesse plus particulièrement à la gestion de l'affichage et qui intègre donc des balises personnalisées qui représentent, en les bonnifiant, des balises classiques que nous connaissons déjà avec le HTML.
  2. La bibliothèque core qui s'intéresse plus particulièrement à des traitements préliminaires comme la gestion globale d'une page ou d'une partie de page, la validation des données saisies, la conversion des valeurs saisies, la gestion des événements, etc.

Dans les lignes suivantes (4-10), nous retrouvons du code HTML classique avec quelque balises personnalisées JSF.

Pour que la page Web puisse gérer convenablement les balises visuelles de JSF, il faut qu'elles soient entourées des balises <h:view>.

La ligne 7 utilise la balise <h:outputText> qui permet d'afficher des valeurs que nous désirons. Ici, nous proposons un texte fixe "en dur" au moyen du paramètre value. Il est possible, et c'est plutôt l'intérêt d'utiliser JSF, de placer du texte variable, issu d'un calcul réalisé par un JavaBean, au travers des expressions EL.

 

Choix du chapitre Bibliothèques de balises JSF

Comme nous venons de le voir, les deux biblothèques de balises JSF sont spécialisées et permettent de gérer deux situations que nous trouvons fréquemment dans les pages Web dynamiques.

  1. La bibliothèque html qui s'interesse plus particulièrement à la gestion de l'affichage et qui intègre donc des balises personnalisées qui représentent, en les bonnifiant, des balises classiques que nous connaissons déjà avec le HTML.
  2. La bibliothèque core qui s'intéresse plus particulièrement à des traitements préliminaires comme la gestion globale d'une page ou d'une partie de page, la validation des données saisies, la conversion des valeurs saisies, la gestion des événements, etc.

Voici ci-dessous, deux tableaux qui nous donnent la plupart des balises JSF utilisées pour permettre la construction d'une page JSP avec une présentation et une gestion de haut niveau.

Actions personnalisées de la bibliothèque html

 

Catégories Balises Desciption
Zone de saisie h:inputHidden Champ caché
h:inputSecret Mot de passe
h:inputText Saisie de texte
h:inputTextArea Zone de texte sur plusieurs lignes
Affichage en sortie h:message Message d'erreur associé à un champ
h:messages Messages d'erreur pour l'ensemble de la page
h:outputFormat Sortie formatée
h:outputLabel ...
h:outputLink Zone de texte en sortie avec lien
h:outputText Zone de texte en sortie
Sélection h:selectBooleanCheckbox
Une seule case à cocher
h:selectManyCheckbox Cases à cocher en multisélection
h:selectManyListbox Zone de liste multisélection
h:selectManyMenu Menu déroulant en multisélection
h:selectOneListbox Zone de liste déroulante
h:selectOneMenu Menu déroulant
h:selectOneRadio Bouton radio
Navigation h:commandButton
Bouton de soumission
h:commandLink Lien vers une autre page
Divers h:dataTable
Tableau de zone de texte
h:form Formulaire
h:graphicImage Image
h:panelGrid Gestion de disposition
h:panelGroup Gestion de disposition avec mise en commun des boutons radio
h:column Colonne de table

 

Actions personnalisées de la bibliothèque core

 

Catégories Balises Desciption
Conversions f:convertDateTime
Conversion de la date
f:convertNumber Conversion des nombres
f:converter Conversion personnalisée
Evénements f:actionListener
Gestion des événements de type Validation
f:valueChangeListener Gestion des événements de type Changement de valeurs
Divers f:attribute
Positionner un attribut d'une balise parente
f:loadBundle Gestion de l'internationalisation
f:param Prise en compte d'un paramètre
f:verbatim Zone de texte brute sans interprétention du compilateur
Sélection f:selectedItem
Un élément de sélection parmi d'autres (dans une liste par exemple)
f:selectedItems Ensemble d'éléments de sélection
Validateurs f:validateDoubleRange
Validation des valeurs réelles par rapport à une limite choisie
f:validateLenght Validation du nombre de caractères que la chaîne peut comporter
f:validateLongRange Validation des valeurs entières par rapport à une limite choisie
f:validator Validation personnalisée
Vues f:facet
Gestion d'une zone délimité de la page Web
f:view Gestion de la page Web en entier.
f:subview Gestion d'une partie de la page Web.

 

Choix du chapitre Gérer les JavaBeans

Nous allons faire une autre application Web qui cette fois-ci fera un minimum de traitement. Cette application Web devra permettre d'exécuter un tout petit jeu qui tire automatiquement un nombre aléatoire à la connexion de la session et dont nous devons retrouver la valeur en proposant des saisies successives. Voici ci-dessous, l'apparence de notre unique page Web. En effet, c'est toujours cette même page qui doit être affichée après la saisie de chacune des valeurs par le client.

Remarquez bien que dans cette page Web, nous n'avons pas intégré de bouton de soumission.
.

Modèle MVC

Cette fois-ci, nous avons besoin d'un modèle qui s'occupera des différents traitements. En effet, nous avons besoin de fabriquer un nombre aléatoire de 1 à 10. Par ailleurs, il faut récupérer la saisie proposé par l'utilisateur et la contrôler suivant le tirage réalisé. Il faut également comptabiliser le nombre de coup effectués avant de trouver le bon chiffre. Pour finir, il faut prévoir un certain nombre de messages adaptés à la situation afin d'orienter l'utilisateur pour son prochain choix.

Afin de respecter le modèle Modèle-Vue-Contrôleur, le traitement est réalisé séparément, à l'aide du JavaBean alea.Nombre. La page d'accueil est cette fois-ci alea.jsp. Encore une fois, c'est la seule page du site. Il n'y aura donc pas de navigation à mettre en oeuvre. Cette page sera en relation directe avec le JavaBean alea.Nombre, ainsi les échanges pourront s'effectuer simplement dans les deux sens. Cette page qui est l'interface visuelle de l'application Web présentera une information adaptée à la situation suivant les événements proposés par l'opérateur.

Constitution de l'application Web et descripteur de déploiement

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
      xmlns="http://java.sun.com/xml/ns/j2ee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <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>
            faces/alea.jsp
        </welcome-file>
    </welcome-file-list>
</web-app>

Fichier de configuration <faces-config.xml>

Je rappelle que ce fichier permet de spécifier les navigations possibles qui peut exister entre les différentes pages Web présentes dans l'application. Ce fichier permet également de spécifier l'ensemble des JavaBeans à mettre en oeuvre afin qu'il soient correctements créés par le contrôleur, et ensuite utilisés directement par les pages JSP concernées.

Encore une fois, nous n'avons pas besoin de nous occuper de la navigation puisque l'application Web ne dispose que d'une seule page. Par contre, nous devons spécifier la mise en oeuvre du JavaBean alea.Nombre.

faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>

<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
   <managed-bean>
      <managed-bean-name>nombre</managed-bean-name>
      <managed-bean-class>alea.Nombre</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
   </managed-bean>
</faces-config>

Pour cela, à l'intérieur de l'élément racine <faces-config>, nous devons ajouter autant de balise <managed-bean> qu'il y a de JavaBean. Ensuite, dans chaque <managed-bean>, il suffit d'indiquer le nom de l'objet (le bean) qui sera créé au travers de l'élément <managed-bean-name>, la classe qu'il représente avec l'élément <managed-bean-class>, et enfin sa durée de vie (sa portée) grâce à l'élément <managed-bean-scope>. Nous retrouvons ici exactement la même démarche que lorsque nous déclarions des beans directement à l'intérieur des pages JSP. L'avantage ici, c'est que la page JSP utilisant la technologie JSF se trouvera grandement alégée.

Je rappelle que le scope d'un bean permet d'indiquer la durée de vie du bean, et qu'il peut prendre 4 valeurs :

  1. session : Certainement, le scope le plus courant. L'objet existe tant que le client consulte l'application Web. L'objet est détruit lorsqu'il quitte le navigateur.

    En réalité, la session est détruite au bout d'une durée d'inactivité. Cette durée est fixée dans le fichier de configuration du serveur.
    §

  2. request : Cette fois-ci, l'objet existe juste pour mettre en relation deux pages Web entre elles, et récupère ainsi les paramètres de la requête.
  3. application : Durée de vie la plus longue. L'objet existe tant que le serveur Web est en activité. Nous pouvons ainsi comptabiliser, par exemple, le nombre de personne qui se connecte sur le site.
  4. none : Cette fois-ci, aucun objet n'est visible dans aucune des pages. Cette désignation est toutefois utile lorsqu'un autre bean (visible) utilise celui-ci. Nous découvrirons ce type d'utilisation ultérieurement.

Attention, le scope page n'existe pas avec les JSF.
.

JavaBean alea.Nombre

Je rappelle que pour avoir le statut de JavaBean, une classe Java doit respecter juste quelques critères. Sinon, mise à part cela, il s'agit d'une classe des plus conventionnelles. Un JavaBean doit :

  1. Posséder au moins un constructeur par défaut parmi les autres constructeurs éventuels. Ou bien la classe ne possède aucun constructeur, ce qui revient à avoir un constructeur par défaut qui ne fait rien par défaut.
  2. Posséder des propriétés. Les attributs doivent être privés. L'accès à ces attributs se fait au travers de méthodes dont la signature est spécifique. Ce principe de connexion s'appelle une propriété. Pour accéder à l'attribut qui vous intéresse, vous devez passer par la méthode dont le nom commence par set suivi du même nom que l'attribut, avec toutefois, la première lettre en majuscule. Pour une lecture de l'attribut, nous appliquons la même démarche, mais cette fois-ci, la méthode commence par get, sauf si le type est booléen, auquel cas, la méthode commence alors par is. Nous pouvons avoir des propriétés qui sont en lecture/écriture ou bien en lecture seule ou écriture seule.

En réalité, dans une propriété, l'attribut peut ne pas exister. Il suffit alors de respecter la signature de ces méthodes particulières pour réaliser les traitements qui vous conviennent.

alea.Nombre
package alea;

public class Nombre {
   private long valeur;
   private long nombreARechercher;
   private int tentative;
   private boolean test;

   public Nombre() {
      nombreARechercher = Math.round(Math.random()*10)+1;
   }
   
   public void setValeur(long valeur) {
      this.valeur = valeur;
      tentative++;
      test = nombreARechercher == valeur;
   }

   public long getValeur() {
      return valeur;
   }

   public int getTentative() {
      return tentative;
   }

   public String getRésultat() {
      if (tentative==0) return "Tentez votre chance";
      if (test) return "Bravo, vous avez trouvé !";
      else return "Non, ce n'est pas le bon chiffre, refaites un essai.";
   }
   
   public String getProgression() {
      if (tentative==0 || test) return "";
      return  "Le nombre est plus "+(nombreARechercher>valeur ? "grand" : "petit");
   }
}

Ce JavaBean alea.Nombre possède quatre propriétés qui seront directement accessibles par la page alea.jsp. Il s'agit des propriétés :

  1. valeur : (lecture/écriture) qui correspond à la valeur saisie par l'opérateur.
  2. tentative : (lecture seule) qui enregistre le nombre de coup effectué par l'opérateur depuis le début de la session.
  3. résultat : (lecture seule) qui propose un message adapté à la situation suivant si on débute la session ou suivant le résultat du tirage.
  4. progression : (lecture seule) qui indique à l'opérateur, après avoir fait au moins une tentative, vers quel type de choix de nombre il faut s'orienter.

Remarquer au passage que les propriétés résultat et progression ne possèdent pas d'attributs correspondants.
.

Par ailleurs, le JavaBean possède des attributs supplémentaires qui ne sont pas accessibles de l'extérieur mais qui sont toutefois bien utiles pour la bonne marche du composant. Ces attributs sont :

  1. nombreARechercher : qui est le nombre que l'opérateur doit retrouver. Ce nombre est calculé par le constructeur par défaut. Ce nombre existe donc dès la création de l'objet. Pour cela, nous faisons appel à la méthode random() de la classe Math. Cette méthode renvoie une valeur aléatoire comprise entre la valeur réelle 0.0 (non comprise) et 1.0 (comprise).
  2. test : attribut booléen qui indique le résultat entre la valeur saisie par l'opérateur et le nombre à rechercher. Ce test est effectué par la propriété valeur, et le résultat est validé si l'opérateur trouve le bon chiffre.

Tous les calculs correspondant aux différents traitements sont donc réalisés à l'intérieur du JavaBean. Ainsi, nous séparons bien la logique du traitement Java de la partie visuelle qui sera mise en oeuvre par la page JSP.

Page d'accueil alea.jsp

Il ne nous reste plus qu'à s'occuper de notre page Web, la partie visuelle du site. La voici ci-dessous. Remarquez au passage qu'elle est très réduite. Par rapport à une page JSP classique, nous n'avons pas besoin de déclarer le bean nombre puisqu'il est déjà réalisé par le fichier de configuration <faces-config.xml>. Par ailleurs, l'accès aux différentes propriétés se fait systématiquement au travers des expressions EL que nous avons déjà rencontés dans les chapitres précédents. Avec ces deux considérations, nous pouvons constater que la page Web s'en trouve extrêmement allégée.

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

<f:view>
   <html>
      <body style="background-color: yellow">
         <h2>Recherche d'un nombre al&eacute;atoire entre 1 et 10</h2>
         <hr />
         <h:form>
            <h4>
               Introduisez votre nombre : 
               <h:inputText value="#{nombre.valeur}" size="2" />
               <h:outputText value="#{nombre.progression}" style="color: red" />
            </h4>
            <h4>Tentatives : <h:outputText value="#{nombre.tentative}" style="color: red" /> fois</h4>
         </h:form>
         <hr />
         <h3><h:outputText value="#{nombre.résultat}" style="color: blue"/></h3> 
      </body>
   </html>
</f:view>

Attention, les EL JSF sont différents des EL JSP. En effet, ces dernières utilisent la notation ${expression} alors qu'ici, avec la technologie JSF, il faut plutôt prendre la notation #{expression}. Le comportement de ces deux syntaxes est très différent. Dans le cas de l'écriture ${...}, l'expression est évalué au moment de la compilation, alors qu'avec l'écriture #{...}, l'expression est évaluée au moment ou JSF effectue le rendu de la page (expression retardée).

Je rappelle que dans le cas d'une connexion avec un bean au travers d'une expression EL, vous spécifier d'abord le nom du bean et ensuite la propriété qui vous intéresse, et vous placez enfin un point de séparation entre ces deux éléments. Ainsi, pour accéder à la propriété valeur du bean nombre, vous écrivez #{nombre.valeur}.

Opérateurs utilisables dans les expressions JSF

Je rappelle qu'il est possible de faire du traitement directement à l'intérieur des expressions jSF. Voici d'ailleurs les opérateurs que vous pouvez utiliser :

Catégorie Opérateurs Exemple
Arithmétique +, -, *, / (ou div), % (ou mod) #{nombre.valeur div 2}
Relationnel == (ou eq), != (ou ne), < (ou lt), > (ou gt), <= (ou le), >= (ou ge) #{nombre.valeur eq 3}
Logique && (ou and), || (ou or), ! (ou not) #{not nombre.existe}
Conditionnel A ? B : C #{nombre.existe ? nombre.valeur : 10}
Vide empty A (A est soit une chaîne String, soit un tableau d'objets, soit une collection de type Map ou List). #{empty nombre.tableau}

 

Lorsque nous consultons cette page Web, nous retrouvons la même ossature que la première page que nous avions créée. Ici toutefois, nous avons rajouter des éléments JSF :

  1. <h:inputText> : Cet élément est une zone d'entrée de texte classique. Cette zone d'entrée doit être en relation avec une propriété en mode lecture/écriture d'un bean. Bien qu'il s'agisse d'une saisie venant de l'opérateur, nous pouvons nous poser la question pourquoi le mode lecture ? Cela permet de proposer automatiquement des valeurs par défaut et surtout de restituer la valeur conservée lorsque la page est réaffichée. Ici, nous utilisons la propriété valeur du bean nombre qui au départ est égale à 0, ceci au travers de l'attribut value. Remarquez au passage la conversion automatique d'une chaîne de caractères vers un entier. L'attribut size permet de spécifier la largeur du champ de saisie. En résumé, lorsque le formulaire est soumis, JSF utilise la méthode setValeur() du JavaBean alea.Nombre pour injecter la valeur du champ <h:inputText> dans l'objet nombre (mapping HTML/Objet !).
  2. <h:form> : Lorsque nous désirons soumettre une valeur, nous devons passer systématiquement par un formulaire. C'est la cas ici avec cette balise <h:form> qui représente effectivement les formulaires. C'est une balise que nous connaissons déjà. Toutefois, la grande nouveauté ici, c'est que nous ne spécifions aucun attribut. En effet, la navigation se fait uniquement dans le fichier de configuration prévu a cet effet <faces-config.xml>. Le gros avantage de ce système, c'est que la page Web devient encore plus claire et qu'il est facile de faire des changements ultérieurs. Tout se trouve dans le même fichier de configuration.

Remarquons ici qu'il n'existe pas de bouton de soumission pour valider notre requête. La validation se fait lorsque l'opérateur appuie sur la touche entrée après avoir saisie la valeur souhaitée. Le gestionnaire de navigation, intégré dans le contrôleur FacesServlet, cherche une règle de navigation applicable au contexte courant, décrite dans le fichier de configuration <faces-config.xml> :
- Si une correspondance est trouvée, alors la page suivante est affichée.
- Sinon la page courante est rechargée.

Nous n'avons pas défini de règle de navigation, c'est donc toujours la même page Web qui se réaffiche avec des informations adaptées à l'évolution de la session.

Lorsque vous avez besoin de plusieurs zones de saisie pour un même formulaire, cette fois-ci, il est nécessaire d'utiliser un bouton de soumission. En effet, les propriétés relatives à ces zones de texte récupèrent juste les valeurs. Le bouton de soumission fait lui appel à la bonne méthode du bean qui s'occupe uniquement du traitement de l'ensemble des valeurs récupérées.

Nous avons également réutilisé les balises <h:outputText> que nous connaissons déjà. Par contre cette fois-ci, au lieu de proposer un texte en dur dans l'attribut value, nous proposons des valeurs variables, toujours au moyen des expression EL et qui sollicitent les propriétés adaptée au message requis. Ainsi, nous affichons respectivement les propriétés progression, tentative et résultat au endroits idoines.

Remarquez au passage que ces balises JSF acceptent des attributs que nous connaisons déjà dans des balises HTML classiques. En effet, nous profitons de l'attribut style (mise en oeuvre des CSS) pour proposer un affichage avec une couleur personnalisée.

Nous avons encore beaucoup de chose à dire sur la gestion des beans au travers du fichier de configuration <faces-config.xml> comme :

  1. Proposer des valeurs par défaut,
  2. Faire appel à un autre bean,
  3. Proposer une liste de valeur,
  4. etc.

Ces aspects seront traités ultérieurement.

 

Choix du chapitre Règles de navigation - navigation statique

Au travers d'un exemple simplissime, nous allons maintenant passer à la navigation entre les pages Web. Dans un premier temps, nous nous intéressons à la navigation statique, c'est-à-dire que le choix des pages est défini dans l'intitulé du bouton et demeure figé. Notre application Web réalise des opérations de base, comme l'addition et la soustraction, sur deux opérandes. Une fois que les opérandes sont définis, il suffit de choisir l'opération à traiter à l'aide du bouton concerné, et une nouvelle page Web apparaît alors, avec le traitement correspondant à l'action souhaitée.

Modèle MVC

Notre modèle doit prendre en compte la navigation entre plusieurs pages. Par ailleurs, le traitement et le stockage des informations est toujours réalisé par un bean que nous nommons ici calcul.Opération. La gestion de ce bean ainsi que la navigation entre les différentes pages Web sont décrites dans le fichier prévu à cet effet <faces-config.xml>.

Constitution de l'application Web et descripteur de déploiement

Cette application Web comporte trois pages jsp avec calcul.jsp comme page d'accueil. Ensuite, le bean calcul.Opération est utilisé pour l'ensemble des trois pages.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app 
   version="2.4"
   xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <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>
            faces/calcul.jsp
        </welcome-file>
    </welcome-file-list>
</web-app>

JavaBean calcul.Opération

En réalité, vu le choix de l'organisation de notre application Web, nous nous servons de ce JavaBean uniquement pour stocker la valeur des opérandes. Le traitement est plutôt réalisé par la page Web appelée. (Ce n'est certainement pas le meilleur choix, mais c'est une solution pour valider la navigation statique).

calcul.Opération
package calcul;

public class Opération {
   private int premier;
   private int deuxième;

   public int getPremier() {
      return premier;
   }

   public void setPremier(int premier) {
      this.premier = premier;
   }   
   
   public int getDeuxième() {
      return deuxième;
   }

   public void setDeuxième(int deuxième) {
      this.deuxième = deuxième;
   }
}

Page d'accueil calcul.jsp

Deux balises JSF déclenchent des actions lorsque l'utilisateur clique sur leur représentation graphique. Il s'agit de la balise <h:commandButton> qui représente un bouton de soumission, et la balise <h:commandLink> qui représente un lien HTML. Lorsque l'utilisateur clique sur l'un ou sur l'autre, une clef de navigation ou outcome est renvoyée. Cette clef peut être statique, en dur dans le code de la page JSP, ou dynamique, calculée par une EL. Vous précisez votre clef de navigation à l'aide de l'attribut action.

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

<f:view>
   <html>
      <body style="color: yellow; background-color: green">
         <h2>Calcul de base</h2>
         <hr />
         <h:form>
            <h4>Opérande 1 : <h:inputText value="#{opération.premier}"  /></h4>
            <h4>Opérande 2 : <h:inputText value="#{opération.deuxième}"  /></h4>
            <hr />
            <p>
               <h:commandButton action="plus" value="+" />
               <h:commandButton action="moins" value="-" />
            </p>
         </h:form>
      </body>
   </html>
</f:view>

Nous avons donc choisi deux boutons pour représenter les actions à réaliser. Les actions sont exprimées directement "en dur" sous forme de chaînes de caractères, respectivement "plus" et "moins". Il s'agit bien d'une navigation statique. L'intitulé du bouton est spécifié au travers de l'attribut value.

Lorsque l'utilisateur clique sur l'un des boutons, alors la clef de navigation (l'outcome) plus ou moins est renvoyée au gestionnaire de navigation JSF. Le gestionnaire de navigation, intégré dans le contrôleur FacesServlet, cherche une règle de navigation applicable au contexte courant, décrite dans le fichier de configuration <faces-config.xml> :
- Si une correspondance est trouvée, alors la page suivante est affichée.
- Sinon la page courante est rechargée.

Fichier de configuration <faces-config.xml>

Je rappelle que ce fichier permet de spécifier les navigations possibles qui peut exister entre les différentes pages Web présentes dans l'application. Ce fichier permet également de spécifier l'ensemble des JavaBeans à mettre en oeuvre afin qu'il soient correctements créés par le contrôleur, et ensuite utilisés directement par les pages JSP concernées.

faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>

<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
   <navigation-rule>
      <from-view-id>/calcul.jsp</from-view-id>
      <navigation-case>
         <from-outcome>plus</from-outcome>
         <to-view-id>/plus.jsp</to-view-id>
      </navigation-case>
      <navigation-case>
         <from-outcome>moins</from-outcome>
         <to-view-id>/moins.jsp</to-view-id>
      </navigation-case>      
   </navigation-rule>
   
   <managed-bean>
      <managed-bean-name>opération</managed-bean-name>
      <managed-bean-class>calcul.Opération</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
   </managed-bean>
</faces-config>

Cette fois-ci, nous avons besoin de décrire les deux types d'éléments : la navigation entre les différentes pages et le JavaBean.

  1. Description du bean : Nous connaissons déjà sa structure. Toutefois, je ferais juste remarquer que nous avons choisi un scope request. En effet, nous ne restons pas sur la page d'accueil, elle n'existe qu'une fois. Il faut juste conserver les valeurs des opérandes, le temps de passage d'une page Web à l'autre, c'est-à-dire le temps de la requête.
  2. Navigation entre les pages : Les règles de navigation sont spécifiées au travers de la balise <navigation-rule>. Vous indiquez ensuite la page source au moyen de la balise <from-view-id>. Vous précisez alors la ou les pages Web à atteindre depuis cette source avec la balise <navigation-case>. Si plusieurs pages peuvent être atteintes depuis cette source, il faut alors spécifier la clef de navigation qui nous orientera sur la bonne page Web à atteindre. C'est la balise <from-outcome> qui récupère la clef de navigation, et c'est la balise <to-view-id> qui indique la page Web à afficher.

Dans cet exemple, nous pouvons éventuellement nous passer de la balise <from-view-id> puisqu'il n'existe qu'une seule source. Il n'y a pas d'ambiguïté possible. Tentez l'expérience, et vous remarquerez que cela fonctionne correctement.

Les pages Web de destination plus.jsp et moins.jsp

Ces pages servent uniquement à la visualisation du résultat. Nous utilisons donc essentiellement des balises <h:outputText>. Je rappelle toutefois que nous pouvons effectuer des traitements avec les expressions EL. C'est ce que nous avons réalisé ici avec une des opérations, + ou -.

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

<f:view>
   <html>
      <body style="color: yellow; background-color: green">
         <h2>Le résultat est : </h2>
         <hr />
         <h4>
            <h:outputText value="#{opération.premier}" />
             + <h:outputText value="#{opération.deuxième}" />
             = <h:outputText value="#{opération.premier + opération.deuxième}" />
         </h4>
      </body>
   </html>
</f:view>
moins.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

<f:view>
   <html>
      <body style="color: yellow; background-color: green">
         <h2>Le résultat est : </h2>
         <hr />
         <h4>
            <h:outputText value="#{opération.premier}" />
             - <h:outputText value="#{opération.deuxième}" />
             = <h:outputText value="#{opération.premier - opération.deuxième}" />
         </h4>
      </body>
   </html>
</f:view>

 

Choix du chapitreNavigation dynamique - Gestion de tableau - Liens

Nous allons reprendre l'application Web qui permet de tirer automatiquement un nombre aléatoire à la connexion de la session, et dont nous devons retrouver la valeur en proposant des saisies successives. Nous allons l'étoffer un petit peu. Cette fois-ci, vous devez retrouver le nombre en trois coups au maximum. Si vous ne trouvez pas la valeur au bout de ces trois coups, une nouvelle page apparaît donnant la valeur que vous deviez trouver ainsi que l'historique des valeurs saisies.

Vous remarquez que notre page d'accueil alea.jsp dispose cette fois-ci de deux boutons. Ainsi, dans la même session, il sera possible maintenant de tout réinitialiser au niveau du bean nombre, et ainsi de tout recommencer. Cela va nous permettre de gérer une navigation dynamique.

Sur la page fin.jsp, nous voyons apparaître deux nouveautés : d'une part la mise en place d'un lien, ainsi que le tracé d'un tableau dynamique dont le nombre de lignes peut varier suivant le conteneur. Ici toutefois, le nombre de ligne est fixe puisque le nombre de coups est systématiquement de trois. Nous ferons en sorte ultérieurement que ce nombre de coups soit variable.

Le clic sur le lien Recommencer de cette page Web permet de naviger vers la page d'accueil. Attention toutefois, c'est plus qu'une navigation simple, puisque les attributs du bean nombre, représentant le traitement des calculs à réaliser, doivent être également réinitilisés.

Modèle MVC

Notre modèle doit prendre en compte la navigation entre les deux pages. Par ailleurs, le traitement des informations est toujours réalisé par le bean alea.Nombre qui comportera toutefois des attributs et des méthodes supplémentaires, vu que notre site s'est enrichi.

 

Constitution de l'application Web et descripteur de déploiement

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
      xmlns="http://java.sun.com/xml/ns/j2ee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <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>
            faces/alea.jsp
        </welcome-file>
    </welcome-file-list>
</web-app>

Notre descipteur de déploiement <web.xml> est totalement identique à celui que nous avions déjà défini. Effectivement, nous faisons appel à la même page d'accueil. Par ailleurs le contrôleur est toujours le même lorsque nous développons un système JSF. Je rappelle que c'est à l'aide du fichier de configuration <faces-config.xml> que nous décrivons la navigation entre les deux pages Web alea.jsp et fin.jsp.

Gestion de l'ensemble du traitement à l'aide du bean alea.Nombre

Nous devons rajouter un certain nombre d'éléments dans notre bean alea.Nombre afin de répondre à ces nouvelles attentes. Nous devons notamment mettre en place le système d'historique, c'est-à-dire les valeurs que l'opérateur à choisi durant ses phases de test. Par ailleurs, nous devons implémenter une navigation dynamique. Effectivement, lorsque l'opérateur clique sur le bouton "Valider votre choix", la page suivante peut aussi bien être de nouveau la page d'accueil comme la page de fin. Cette page de fin est sollicité lorsque nous sommes sur le troisième coup et que nous n'avons toujours pas trouvé la bonne valeur. Par ailleurs, il faut penser à réinitialiser les valeurs des attributs lorsque l'opérateur demande à tout recommencer. Encore une fois, cela impose de mettre en place une navigation dynamique puisque nous devons faire appel à un traitement particulier sur le bean avant de réactiver la page d'accueil.

alea.Nombre
package alea;

import java.util.*;

public class Nombre {
   private int valeur;
   private int nombreARechercher;
   private int tentative;
   private boolean test;
   private ArrayList<Integer> historique;

   public Nombre() {
      init();
   }
   
   private void init() {
      nombreARechercher = (int)(Math.random()*10)+1;
      tentative = 0;
      valeur = 0;
      historique = new ArrayList<Integer>();
   }
   
   public void setValeur(int valeur) {
      this.valeur = valeur;
      tentative++;
      test = nombreARechercher == valeur;
      historique.add(valeur);
   }

   public int getValeur() {
      return valeur;
   }

   public int getTentative() {
      return tentative;
   }

   public String getRésultat() {
      if (tentative==0) return "Tentez votre chance";
      if (test) return "Bravo, vous avez trouvé !";
      else return "Non, ce n'est pas le bon chiffre, refaites un essai.";
   }
   
   public String getProgression() {
      if (tentative==0 || test) return "";
      return  "Le nombre est plus "+(nombreARechercher>valeur ? "grand" : "petit");
   }
   
   public String fini() {
      return !test && tentative>=3 ? "finir" : "continuer";
   }
   
   public String recommencer() {
      init();
      return "recommencer";
   }

   public ArrayList<Integer> getHistorique() {
      return historique;
   }

   public int getNombreARechercher() {
      return nombreARechercher;
   }
}
  1. Pour la gestion de l'historique, nous prenons un ArrayList qui gère les tableaux dynamiques de type quelconque. Par définition, il est donc de taille variable. JSF utilise plusieurs type de conteneurs : un conteneur séquenciel qui hérite de la classe ancêtre List, comme c'est le cas avec ArrayList, et un conteneur associatif qui hérite de la classe ancêtre Map. Il permet également, tout simplement, de gérer des tableaux statiques. Attention toutefois, il s'agit systématiquement de tableaux d'objets (nous exécuterons le même exemple avec un tableau statique tout à l'heure).
  2. Puisque les attributs du bean peuvent être initialisés de plusieurs manières, j'ai préféré mettre en place une méthode privée spécifique init(). Cette méthode est lancée à partir du constructeur, mais aussi par la méthode recommencer().
  3. La navigation dynamique est assurée respectivement par les méthodes fini() et recommencer(). La méthode fini() évalue le résultat du test avec le nombre de tentative réalisée. Si le nombre de tentative atteint 3 et que le test n'est pas bon, la méthode renvoie la chaîne de caractères "finir". Dans le cas contraire, la méthode fini() renvoie la chaîne de caractères "continuer". La méthode recommencer() initialise les attributs du bean avant de renvoyer la chaîne de caractère "recommencer" qui sera utile pour revenir sur la page d'accueil au cas où.

Page d'accueil du site alea.jsp

Par rapport à la page d'accueil initiale, nous devons juste mettre en oeuvre les deux boutons "Valider votre choix" et "Tout recommencer" qui vont servir à la navigation.

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

<f:view>
   <html>
      <body style="background-color: yellow">
         <h2>Recherche d'un nombre al&eacute;atoire compris entre 1 et 10</h2>
         <hr />
         <h:form>
            <h4>
               Introduisez votre nombre : 
               <h:inputText value="#{nombre.valeur}" size="2"  />
               <h:outputText value="#{nombre.progression}" style="color: red" />
            </h4>
            <h4>Tentatives : <h:outputText value="#{nombre.tentative}" style="color: red" /> fois</h4>
            <p>
               <h:commandButton action="