Composants et facelet

//TODO adapter à Netbeans et JSF2.2

Structure et syntaxe

Nous allons aborder la structure d'une Facelet en la comparant avec la structure d'une technologie que nous connaissons déjà : la page JSP.

Généralités

La différence la plus importante se situe au niveau du contenu. Alors qu'une page JSP est un objet complexe, pouvant contenir à la fois des balises JSP ou JSTL et du code Java par l'intermédiaire de scriptlets, une Facelet est un fichier XML pur ! Elle ne peut par conséquent contenir que des balises, et il est impossible d'y inclure des scriplets : le Java est donc définitivement banni de la vue avec cette technologie.

Au niveau du format et du rendu, la situation n'a pas changé : tout comme les servlets et les pages JSP sont le plus souvent utilisées pour générer des pages HTML, c'est le langage XHTML qui est le plus utilisé pour créer des Facelets (XHTML étant une version de la syntaxe HTML conforme au standard XML). Toutefois, sachez que le framework JSF est markup-agnostique : cela signifie qu'il peut s'adapter à n'importe quel langage, à partir du moment où ce langage respecte la structure décrite par le standard XML. Ainsi, il est également possible de construire une Facelet avec du langage XML pur, par exemple pour créer un flux RSS, ou avec le langage XUL pour n'en citer qu'un.

Au niveau de l'aspect extérieur du fichier, une Facelet porte en général une de ces trois extensions : .jsf,.xhtml ou .faces. Il n'existe pas une unique extension pour la simple raison que nous venons de découvrir, à savoir le fait qu'une Facelet n'est rien d'autre qu'un document XML. Dans l'absolu, c'est par défaut l'extension .xml qui pourrait être utilisée... Cela dit, il est utile de pouvoir reconnaître facilement une Facelet parmi d'autres fichiers du même type, pour la démarquer du reste. Voilà pourquoi ces trois extensions ont de facto été retenues par les développeurs. Ne vous inquiétez pas, nous découvrirons très bientôt comment le framework est capable de savoir quelles extensions de fichiers lui sont destinées.

Ainsi, voici la structure à vide d'une Facelet :

1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml"> 
3    ...
4</html>

Vous retrouvez bien le doctype HTML, suivi de la balise <html> conforme au standard XHTML. Bref, en apparence ce n'est qu'une page web toute simple !

Bibliothèques et balises

Une Facelet est donc constituée de balises, dont la syntaxe est très similaire à celles des balises JSTL utilisées dans une page JSP.

Avant de poursuivre sur la forme des balises JSF, intéressons-nous aux bibliothèques. Tout comme il existe des directives permettant d'importer des bibliothèques de balises JSTL dans une page JSP, il existe un moyen pour inclure les bibliothèques de balises JSF dans une Facelet. Toutefois, il vous faut oublier le concept même de la directive JSP : il s'agissait là littéralement d'un ordre donné au conteneur, lui précisant comment il devait gérer une page JSP et générer sa servlet Java associée. Voilà pourquoi il était non seulement possible via une directive d'inclure des bibliothèques, mais également d'importer des classes Java, d'activer ou non les sessions HTTP, les expressions EL, etc.

Dans une Facelet, une bibliothèque de balises est incluse via l'ajout d'un attribut xmlns à la balise<html> qui ouvre le corps de la page. Il s'agit là d'un namespace XML. Je vous invite à vous renseigner sur cette notion si vous souhaitez comprendre comment cela fonctionne en détail. En ce qui nous concerne, nous allons simplement retenir qu'un tel attribut permet de déclarer une bibliothèque dans une Facelet ! Le framework JSF intègre nativement trois bibliothèques standard : HTMLCore et UI. Voici comment les inclure dans une Facelet :

1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml" 
3       xmlns:f="http://java.sun.com/jsf/core" 
4       xmlns:h="http://java.sun.com/jsf/html"
5       xmlns:ui="http://java.sun.com/jsf/facelets"> 
6    ...
7</html>

Vous reconnaissez ici le système de liens utilisé également par la JSTL. De même, vous retrouvez le système de préfixe identifiant une bibliothèque : traditionnellement, h: désigne la bibliothèque HTML, ui: la bibliothèque de templating et de composition, et f: la bibliothèque Core. Au passage, ne confondez pas cette dernière avec la bibliothèque Core de la JSTL, nous parlons bien ici de balises JSF !

Pour terminer et revenir sur les balises JSF à proprement parler, sur la forme elles ressemblent comme deux gouttes d'eau aux balises JSTL. Par exemple, voici une balise issue de la bibliothèque HTML :

1<h:outputLabel for="confirmation">Confirmation du mot de passe <span class="requis">*</span></h:outputLabel>

Vous pouvez d'ores et déjà remarquer les similitudes avec les balises JSTL : il est possible d'y préciser des attributs, une balise possède un corps, il est possible d'y inclure d'autres balises, etc.

Dans nos exemples à venir, nous allons utiliser de nombreuses balises qui vous sont encore inconnues. Si j'ai pris le temps de vous décrire les balises JSP et JSTL les plus couramment utilisées lors de nos premiers pas avec Java EE, je ne vais cette fois pas rédiger d'inventaire des balises JSF, et ce pour trois raisons :

  1. vous êtes à l'aise avec le concept de balises, vous savez comment elles se construisent ;

  2. vous êtes à l'aise avec la recherche d'informations, vous êtes capables de trouver et parcourir les ressources et documentations par vous-mêmes ;

  3. les bibliothèques standard de JSF contiennent beaucoup de balises, certaines dédiées à des tâches bien différentes de ce que vous connaissez et avez manipulé jusqu'à présent, et en faire l'inventaire détaillé et expliqué par l'exemple serait chronophage et en fin de compte peu efficace.

Pour vous faciliter la tâche, je vous communique quelques ressources extrêmement utiles :
la documentation officielle des balises JSF, par Oracle ; une documentation similaire, qui date un peu mais qui est présentée de manière un peu plus conviviale que le format Javadoc, par JSFToolbox ; la page JSF de Stackoverflow, contenant des généralités, des exemples basiques et une liste de liens fournie et à jour sur le sujet.

Gardez ces liens accessibles dans les favoris de votre navigateur, vous allez en avoir besoin pour découvrir toutes les balises proposées par JSF !

Expressions EL

Une Facelet peut, tout comme une page JSP, contenir des expressions EL. Toutefois, celles-ci sont un peu différentes de celles que nous avons utilisées jusqu'à présent. Voyez dans cet exemple la forme d'une expression EL avec JSF :

1#{inscrireBean.utilisateur.motDePasse}

Vous allez me dire, à part le symbole # qui remplace le symbole $ auparavant utilisé, a priori le reste ne change pas : les accolades, l'accès aux beans, à ses propriétés, l'opérateur . en guise de séparateur... Eh bien en réalité, c'est un peu plus compliqué que ça. Pour mieux comprendre, faisons un rapide historique de la technologie EL :

  • en juin 2002, la première version de la JSTL paraît et introduit le concept des expressions EL pour la toute première fois. Celles-ci étaient alors construites pour appeler les méthodes getter de JavaBeans, uniquement via la syntaxe ${...} et uniquement depuis les balises JSTL ;

  • en novembre 2003, la seconde mouture de la technologie JSP fait son apparition et les expressions EL deviennent partie intégrante du standard J2EE 1.4. La JSTL en version 1.1 ne contient alors plus la technologie EL, et la syntaxe ${...} fonctionne désormais en dehors des balises JSTL, dans le corps des pages JSP ;

  • en mars 2004, la première version de JSF est publiée, et introduit le concept des expressions EL dites "différées". Il s'agissait alors d'expressions sous la forme #{...} et ne fonctionnant que dans des balises JSF. La principale différence avec les expressions de la forme ${...} décrites par le standard JSP est qu'elles permettent non seulement d'appeler les méthodes getter des beans, mais également leurs méthodes setter ;

  • en mai 2005, les deux technologies EL coexistantes sont combinées en une seule spécification : les expressions EL sont alors dites "unifiées", et font partie intégrante du standard Java EE 5. La syntaxe #{...} devient de fait utilisable également depuis une page JSP, mais y est toujours limitée à l'accès aux méthodes getter, seul l'usage depuis JSF permet d'appeler les méthodessetter ;

  • en décembre 2009, une évolution des expressions EL est introduite avec le standard Java EE 6. Celles-ci permettent désormais, depuis la syntaxe #{...}, d'appeler n'importe quelle méthode d'un bean, et non plus seulement ses getters/setters. C'est par ailleurs à cette occasion que les Facelets sont devenues partie intégrante du standard Java EE 6.

De cette longue épopée, vous devez retenir deux choses importantes :

  1. il est possible d'utiliser la syntaxe #{...} depuis vos pages JSP ! Je ne vous l'ai pas présentée plus tôt pour ne pas vous embrouiller, et surtout pour ne pas vous voir appeler des méthodes Java dans tous les sens sans comprendre ce que MVC implique et impose, ni comprendre comment doit être construit un bean...

  2. nous allons dorénavant utiliser la syntaxe #{...} avec JSF, car contrairement à ce que nous faisions avec la syntaxe ${...} depuis nos pages JSP, nous allons avoir besoin d'initialiser des valeurs et non plus seulement d'accéder en lecture à des valeurs.

Vous voilà au point sur les aspects superficiels de la technologie Facelet. Il est grand temps maintenant de passer derrière les rideaux, et de découvrir comment tout cela fonctionne dans les coulisses...

Comment ça marche ?

Avant tout, et parce que vous en aurez forcément un jour besoin si vous travaillez avec JSF, voici l'outil le plus utile pour comprendre les rouages du système : la documentation officielle du framework JSFdans sa version actuelle (2.1).

Vous savez déjà comment fonctionne une page JSP, mais un petit rappel ne fait jamais de mal. La technologie JSP fournit un langage de templating, permettant de créer des pages qui sont - par un procédé que vous connaissez - traduites en servlets Java, puis compilées. Pour faire court, le corps d'une page JSP devient l'équivalent de la méthode service(), la méthode mère des méthodesdoGet() et doPost(). Les balises JSP et JSTL qui y sont utilisées sont directement transformées en code Java et intégrées dans la servlet générée, vous pouvez d'ailleurs vous en rendre compte par vous-mêmes en allant regarder le code des servlets auto-générées par Tomcat dans nos précédents exemples.

Dans une Facelet par contre, qui comme nous venons de le voir n'est qu'un fichier XML, les balises JSF ne sont que des appels à des composants JSF qui sont entièrement autonomes. Autrement formulé, lorsqu'un composant JSF est appelé, il génère son propre rendu dans son état courant. Le cycle de vie des composants JSF n'a par conséquent aucune relation avec le cycle de vie d'une page JSP et de sa servlet auto-générée. Une Facelet est donc une page constituée d'une suite d'appels à des composants JSF (réalisés par l'intermédiaire de balises), et ceux-ci forment ce que l'on appelle un arbre de composants, ou component tree en anglais.

Ainsi, ne vous laissez pas tromper par les apparences : même si JSF vous permet de travailler avec des balises JSF qui ressemblent comme deux gouttes d'eau à des balises JSTL, retenez bien que JSF ne fonctionne pas comme fonctionnaient nos exemples MVC basés sur des servlets et des JSP. Ces similitudes ont par contre un avantage certain : puisque vous connaissez déjà MVC avec les JSP, vous pouvez attaquer le développement avec JSF très rapidement !

En prenant un peu de recul, JSF s'apparente dans son fonctionnement à Swing ou AWT : c'est unframework qui fournit une collection de composants standards et réutilisables, permettant la création d'interfaces utilisateur (web, en l'occurrence). À la différence des JSP, les Facelets JSF conservent un état (on dit alors que la vue est stateful) : ce sont les composants autonomes appelés par l'intermédiaire des balises contenues dans les Facelets qui permettent de maintenir cet état. De la même manière que Swing et AWT, les composants JSF suivent le pattern de l'objet composite pour gérer un arbre de composants : en clair, cela signifie à la fois qu'un objet conteneur contient un composant, et qu'un objet conteneur est lui-même un composant. La vue lie ces composants graphiques à la page XHTML, et permet ainsi au développeur de directement lier des champs HTML d'interaction utilisateur (saisie de données, listes, etc.) à des propriétés de beans, et des boutons à leurs méthodes d'action.

Un processus en 6 étapes

Arrêtons les comparaisons avec d'autres solutions, et étudions concrètement le fonctionnement duframework. Avec JSF, le traitement d'une requête entrant sur le serveur est découpé en six étapes, que nous allons parcourir sommairement :

1. La restauration de la vue
La requête entrante est redirigée vers l'unique servlet jouant le rôle de super-contrôleur, laFacesServlet. Celle-ci examine son contenu, en extrait le nom de la page ciblée et détermine s'il existe déjà une vue associée à cette page (eh oui, rappelez-vous bien qu'avec JSF la vue conserve un état). Voilà pourquoi cette étape s'intitule "restauration de la vue" : il s'agit en réalité de restaurer les éventuels composants déjà chargés si l'utilisateur a déjà accédé à la page par le passé.

La FacesServlet va donc chercher les composants utilisés par la vue courante. Si la vue n'existe pas déjà, elle va la créer. Si elle existe déjà, elle la réutilise. La vue contient tous les composants de l'interface utilisateur intervenant dans la page. La vue (c'est-à-dire l'ensemble des composants qui y interviennent, et donc son état) est sauvegardée dans l'objet FacesContext.

2. L'application des valeurs contenues dans la requête
Arrivés à cette étape, les composants de la vue courante ont tout juste été récupérés ou créés depuis l'objet FacesContext. Chacun d'eux va maintenant récupérer la valeur qui lui est assignée depuis les paramètres de la requête, ou éventuellement depuis des cookies ou headers.

Ces valeurs vont alors être converties. Ainsi, si un champ est lié à une propriété de type Integer, alors son contenu va être converti en Integer. Si cette conversion échoue, un message d'erreur va être placé dans le FacesContext, et sera utilisé lors du futur rendu de la réponse.

À noter qu'à cette étape peut intervenir la "prise en charge immédiate des événements" : cela veut dire que si un composant est marqué comme tel, sa valeur va directement être convertie puis validée dans la foulée. Si aucun composant n'arbore cette propriété, alors les valeurs de tous les composants sont d'abord converties, puis intervient ensuite l'étape de validation sur l'ensemble des valeurs.

3. La validation des données
Les valeurs tout juste converties vont ensuite être validées, en suivant les règles de validation définies par le développeur. Si la validation d'une valeur échoue, un message d'erreur est ajouté au FacesContext, et le composant concerné est marqué comme "invalide" par JSF. La prochaine étape est alors directement le rendu de la réponse, il n'y aura aucune autre étape intermédiaire.

Si les valeurs sont correctes vis-à-vis des règles de validation en place, alors la prochaine étape est la mise à jour des valeurs du modèle.

4. La mise à jour des valeurs du modèle
Les composants peuvent être directement liés, par l'intermédiaire des balises présentes dans la vue, à des propriétés de beans. Ces beans sont qualifiés de managed-beans ou backing-beans, car ils sont gérés par JSF et la vue s'appuie sur eux. Si de tels liens existent, alors les propriétés de ces beans sont mises à jour avec les nouvelles valeurs des composants correspondants, fraîchement validées. Puisque la validation a eu lieu en premier lieu, le développeur est certain que les données enregistrées dans le modèle sont valides, au sens format du champ du formulaire. Il n'est par contre pas exclu que les données ne soient pas valides d'un point de vue de ce qu'attend le code métier de l'application, mais c'est tout à fait normal, puisque cette étape est la suivante dans le processus...

5. L'appel aux actions, le code métier de l'application
Les actions associées à la soumission du formulaire sont alors appelées par JSF. Il s'agit enfin de l'entrée en jeu du code métier : maintenant que les données ont été converties, validées et enregistrées dans le modèle, elles peuvent être utilisées par l'application.

La fin de cette étape se concrétise par la redirection vers la vue correspondante, qui peut dépendre ou non du résultat produit par le code métier. Il s'agit donc de définir la navigation au sein des vues existantes, ce qui est réalisé directement depuis le bouton de validation dans la page, ou depuis un fichier de configuration XML externe nommé faces-config.xml.

6. Le rendu de la réponse
La dernière et ultime étape est le rendu de la réponse. La vue définie dans la navigation est finalement affichée à l'utilisateur : tous les composants qui la composent effectuent alors leur propre rendu, dans leur état courant. La page HTML ainsi générée est finalement envoyée au client, mais ça, vous vous en doutiez !

Voilà comment se déroule le traitement d'une requête avec JSF. Comme vous pouvez le voir, ça change du mode de traitement linéaire que nous avions adopté dans nos exemples MVC faits maison, notamment au niveau de la possibilité de prise en charge immédiate d'un événement qui permet le court-circuitage du processus global pour un composant en particulier ! Si tout ceci est encore très flou dans votre tête, c'est normal : beaucoup de choses vous semblent encore bien abstraites. Ne vous découragez surtout pas, car comprendre le fonctionnement de JSF est l'effort le plus intense qu'il vous faudra fournir. Dès lors que vous aurez assimilé comment se goupille toute cette mécanique, vous aurez fait plus de la moitié du chemin vers l'adoption de JSF ! ;)

Pour vous aider à bien comprendre, je vous propose de découvrir ce processus dans un exemple pratique très simple. Mais avant cela, je vous propose de vous détendre un peu en découvrant une petite astuce sous Eclipse, permettant de préparer rapidement votre espace de travail au développement de Facelets, les fameuses pages que nous allons créer à la place de nos pages JSP.

Créer un template de Facelet par défaut avec Eclipse

Avec Eclipse, il est possible de définir quel sera le contenu généré par défaut lors de la création d'un nouveau fichier. Pour préparer facilement nos vues JSF, il nous suffit donc de créer un nouveau type de fichier nommé "Facelet" et de personnaliser son contenu par défaut. Pour ce faire, rendez-vous dans les préférences d'Eclipse, puis suivez Web > HTML Files > Editor > Templates et cliquez enfin sur New.

Entrez alors comme nom "New Facelet Page", puis sélectionnez le contexte "New HTML", entrez comme description "Creates a new Facelet page", puis copiez le code suivant dans le champ pattern et validez enfin en cliquant sur OK (voir la figure suivante).

1<!DOCTYPE html>
2<html lang="fr"
3    xmlns="http://www.w3.org/1999/xhtml"
4    xmlns:f="http://java.sun.com/jsf/core"
5    xmlns:h="http://java.sun.com/jsf/html"
6    xmlns:ui="http://java.sun.com/jsf/facelets">
7    <h:head>
8        <title>Insérer le titre ici</title>
9    </h:head>
10    <h:body>
11        ${cursor}
12    </h:body>
13</html>
Image utilisateur

Une fois cette configuration en place, vous pourrez simplement créer une nouvelle Facelet prête à être codée ! Il vous suffira de faire un clic droit dans un projet, puis de suivre New > HTML File. Puis de donner un nom à votre fichier et de cliquer sur Next dans la fenêtre qui s'affiche alors, avant de choisir votre template fraîchement créé dans la liste qui s'affiche enfin et de valider en cliquant sur Finish, comme indiqué à la figure suivante.

Image utilisateur

Le nouveau fichier que vous venez de créer contient alors automatiquement le code de base que vous avez défini plus tôt dans les options d'Eclipse.

Comments