12 Traitement et navigation Au chapitre précédent, nous avons vu comment créer des pages web avec différentes technologies (HTML, JSP, JSTL, etc.) en insistant sur le fait que JSF est la spécification conseillée pour écrire des applications web modernes avec Java EE. Cependant, créer des pages contenant des composants graphiques ne suffit pas : ces pages doivent interagir avec un backend (un processus en arrière-plan), il faut pouvoir naviguer entre les pages et valider et convertir les données. JSF est une spécification très riche : les beans gérés permettent d’invoquer la couche métier, de naviguer dans votre application, et, grâce à un ensemble de classes, vous pouvez convertir les valeurs des composants ou les valider pour qu’ils correspondent aux règles métiers. Grâce aux annotations, le développement de convertisseurs et de validateurs personnalisés est désormais chose facile. JSF 2.0 apporte la simplicité et la richesse aux interfaces utilisateurs dynamiques. Il reconnaît nativement Ajax en fournissant une bibliothèque JavaScript permettant d’effectuer des appels asynchrones vers le serveur et de rafraîchir une page par parties. La création d’interfaces utilisateurs, le contrôle de la navigation dans l’application et les appels synchrones ou asynchrones de la logique métier sont possibles parce que JSF utilise le modèle de conception MVC (Modèle-Vue-Contrôleur). Chaque partie est donc isolée des autres, ce qui permet de modifier l’interface utilisateur sans conséquence sur la logique métier et vice versa. Le modèle MVC JSF et la plupart des frameworks web encouragent la séparation des problèmes en utilisant des variantes du modèle MVC. Ce dernier est un modèle d’architecture permettant d’isoler la logique métier de l’interface utilisateur car la première ne se © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves 386 Java EE 6 et GlassFish 3 mélange pas bien avec la seconde : leur mélange produit des applications plus difficiles à maintenir et qui supportent moins bien la montée en charge. Dans la section "JavaServer Pages" du chapitre précédent, nous avons vu une page JSP qui contenait à la fois du code Java et des instructions SQL : bien que ce soit techniquement correct, imaginez la difficulté de maintenir une telle page... Elle mélange deux types de développement différents (celui de concepteur graphique et celui de programmeur métier) et pourrait finir par utiliser bien plus d’API encore (accès aux bases de données, appels d’EJB, etc.), par gérer les exceptions ou par effectuer des traitements métiers complexes. Avec MVC, l’application utilise un couplage faible, ce qui facilite la modification de son aspect visuel ou des règles métiers sous-jacentes sans pour autant affecter l’autre composante. Comme le montre la Figure 12.1, la partie "modèle" de MVC représente les données de l’application ; la "vue" correspond à l’interface utilisateur et le "contrôleur" gère la communication entre les deux. Figure 12.1 Client Server Requête HTTP Le modèle de conception MVC. Navigateur Contrôleur (FacesServlet) Réponse HTTP crée et gère manipule et redirige Modèle (backing bean) accède Vue (pages XHTML) Le modèle est représenté par le contenu, qui est souvent stocké dans une base de données et affiché dans la vue ; il ne se soucie pas de l’aspect que verra l’utilisateur. Avec JSF, il peut être formé de backing beans, d’appels EJB, d’entités JPA, etc. La vue JSF est la véritable page XHTML (XHTML est réservé aux interfaces web, mais il pourrait s’agir d’un autre type de vue, comme WML pour les dispositifs mobiles). Comme au chapitre précédent, une vue fournit une représentation graphique d’un modèle et un modèle peut avoir plusieurs vues pour afficher un livre sous forme de formulaire ou de liste, par exemple. Lorsqu’un utilisateur manipule une vue, celle-ci informe un contrôleur des modifications souhaitées. Ce contrôleur se charge alors de rassembler, convertir et valider les données, appelle la logique métier puis produit le contenu en XHTML. Avec JSF, le contrôleur est un objet FacesServlet. © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves Chapitre 12 Traitement et navigation 387 FacesServlet FacesServlet est une implémentation de javax.servlet.Servlet qui sert de contrô- leur central par lequel passent toutes les requêtes. Comme le montre la Figure 12.2, la survenue d’un élément (lorsque l’utilisateur clique sur un bouton, par exemple) provoque l’envoi d’une notification au serveur via HTTP ; celle-ci est interceptée par javax.faces.webapp.FacesServlet, qui examine la requête et exécute différentes actions sur le modèle à l’aide de beans gérés. Figure 12.2 Cycle de vie Interactions de FacesServlet. 3. Traitement en 6 étapes FacesContext 2. Passe le contrôle au cycle de vie 1. Crée un FaceContext Bouton Événement FacesServlet En coulisse, la FacesServlet prend les requêtes entrantes et donne le contrôle à l’objet javax.faces.lifecycle.Lifecycle. À l’aide d’une méthode fabrique, elle crée un objet javax.faces.context.FacesContext qui contient et traite les informations d’état de chaque requête. L’objet Lifecycle utilise ce FacesContext en six étapes (décrites au chapitre précédent) avant de produire la réponse. Les requêtes qui doivent être traitées par la FacesServlet sont redirigées à l’aide d’une association de servlet dans le descripteur de déploiement. Les pages web, les beans gérés, les convertisseurs, etc. doivent être assemblés avec le fichier web.xml du Listing 12.1. Listing 12.1 : Fichier web.xml définissant la FacesServlet <?xml version=’1.0’ encoding=’UTF-8’?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.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> © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves 388 Java EE 6 et GlassFish 3 <url-pattern>*.faces</url-pattern> </servlet-mapping> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> </web-app> Ce fichier définit la javax.faces.webapp.FacesServlet en lui donnant un nom (Faces Servlet, ici) et une association. Dans cet exemple, toutes les requêtes portant l’extension .faces sont associées pour être gérées par la servlet – toute requête de la forme http://localhost:8080/ chapter10-1.0/newBook.faces sera donc traitée par JSF. Vous pouvez également configurer quelques paramètres spécifiques à JSF dans l’élément <context-param> (voir Tableau 12.1). Tableau 12.1 : Paramètres de configuration spécifiques à JSF Paramètre Description javax.faces.CONFIG_FILES Définit une liste de chemins de ressources liées au contexte dans laquelle JSF recherchera les ressources. javax.faces.DEFAULT_SUFFIX Permet de définir une liste de suffixes possibles pour les pages ayant du contenu JSF (.xhtml, par exemple). javax.faces.LIFECYCLE_ID Identifie l’instance LifeCycle utilisée pour traiter les requêtes JSF. javax.faces.STATE_SAVING_ METHOD Définit l’emplacement de sauvegarde de l’état. Les valeurs possibles sont server (valeur par défaut qui indique que l’état sera généralement sauvegardé dans un objet HttpSession) et client (l’état sera sauvegardé dans un champ caché lors du prochain envoi de formulaire). javax.faces.PROJECT_STAGE Décrit l’étape dans laquelle se trouve cette application JSF dans le cycle de vie (Development, UnitTest, SystemTest ou Production). Cette information peut être utilisée par une implémentation de JSF pour améliorer les performances lors de la phase de production en utilisant un cache pour les ressources, par exemple. javax.faces.DISABLE_FACELET_ JSF_VIEWHANDLER Désactive Facelets comme langage de déclaration de page (PDL). javax.faces.LIBRARIES Liste des chemins qui seront considérés comme une bibliothèque de marqueurs Facelets. © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves Chapitre 12 Traitement et navigation 389 FacesContext JSF définit la classe abstraite javax.faces.context.FacesContext pour représenter les informations contextuelles associées au traitement d’une requête et à la production de la réponse correspondante. Cette classe permet d’interagir avec l’interface utilisateur et le reste de l’environnement JSF. Pour y accéder, vous devez soit utiliser l’objet implicite facesContext dans vos pages (les objets implicites ont été présentés au chapitre précédent), soit obtenir une référence dans vos beans gérés à l’aide de la méthode statique getCurrentInstance() : celle-ci renverra l’instance de FacesContext pour le thread courant et vous pourrez alors invoquer les méthodes du Tableau 12.2. Tableau 12.2 : Quelques méthodes de FacesContext Méthode Description addMessage Ajoute un message d’erreur. getApplication Renvoie l’instance Application associée à cette application web. getAttributes Renvoie un objet Map représentant les attributs associés à l’instance FacesContext. getCurrentInstance Renvoie l’instance FacesContext pour la requête traitée par le thread courant. getMaximumSeverity Renvoie le niveau d’importance maximal pour tout FacesMessage mis en file d’attente. getMessages Renvoie une collection de FacesMessage. getViewRoot Renvoie le composant racine associé à la requête. release Libère les ressources associées à cette instance de FacesContext. renderResponse Signale à l’implémentation JSF que le contrôle devra être transmis à la phase Render response dès la fin de l’étape de traitement courante de la requête, en ignorant les étapes qui n’ont pas encore été exécutées. responseComplete Signale à l’implémentation JSF que la réponse HTTP de cette requête a déjà été produite et que le cycle de vie du traitement de la requête doit se terminer dès la fin de l’étape en cours. © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves 390 Java EE 6 et GlassFish 3 Configuration de Faces La FacesServlet est interne aux implémentations de JSF ; bien que vous n’ayez pas accès à son code, vous pouvez la configurer avec des métadonnées. Vous savez désormais qu’il existe deux moyens d’indiquer des métadonnées avec Java EE 6 : les annotations et les descripteurs de déploiement XML (/WEB-INF/faces-config. xml). Avant JSF 2.0, le seul choix possible était XML mais, désormais, les beans gérés, les convertisseurs, les moteurs de rendu et les validateurs pouvant utiliser les annotations, les fichiers de configuration XML sont devenus facultatifs. Nous conseillons l’emploi des annotations mais, pour montrer à quoi ressemble un fichier faces-config.xml, le Listing 12.2 définit une locale et un ensemble de messages pour l’internationalisation et certaines règles de navigation. Nous verrons ensuite comment naviguer avec et sans faces-config.xml. Listing 12.2 : Extrait d’un fichier faces-config.xml <?xml version=’1.0’ encoding=’UTF-8’?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" version="2.0"> <application> <locale-config> <default-locale>fr</default-locale> </locale-config> <resource-bundle> <base-name>messages</base-name> <var>msg</var> </resource-bundle> </application> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>doCreateBook-success</from-outcome> <to-view-id>/listBooks.htm</to-view-id> </navigation-case> </navigation-rule> </faces-config> Beans gérés Comme on l’a indiqué plus haut, le modèle MVC encourage la séparation entre le modèle, la vue et le contrôleur. Avec Java EE, les pages JSF forment la vue et la © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves Chapitre 12 Traitement et navigation 391 est le contrôleur. Les beans gérés, quant à eux, sont une passerelle vers le modèle. Les beans gérés sont des classes Java annotées. Ils constituent le cœur des applications web car ils exécutent la logique métier (ou la délèguent aux EJB, par exemple), gèrent la navigation entre les pages et stockent les données. Une application JSF typique contient un ou plusieurs beans gérés qui peuvent être partagés par plusieurs pages. Les données sont stockées dans les attributs du bean géré, qui, en ce cas, est également appelé "backing bean". Un backing bean définit les données auxquelles est lié un composant de l’interface utilisateur (la cible d’un formulaire, par exemple). Pour établir cette liaison, on utilise EL, le langage d’expressions. FacesServlet Écriture d’un bean géré Écrire un bean géré est aussi simple qu’écrire un EJB ou une entité JPA puisqu’il s’agit simplement de créer une classe Java annotée par @ManagedBean (voir Listing 12.3) – il n’y a nul besoin de créer des entrées dans faces-config.xml, de créer des classes utilitaires ou d’hériter d’une classe quelconque : JSF 2.0 utilisant également le mécanisme de configuration par exception, une seule annotation suffit pour utiliser tous les comportements par défaut et pour déployer une application web utilisant un bean géré. Listing 12.3 : Bean géré simple @ManagedBean public class BookController { private Book book = new Book(); public String doCreateBook() { createBook(book); return "listBooks.xhtml"; } } // Constructeurs, getters, setters Le Listing 12.3 met en évidence le modèle de programmation d’un bean géré : il stocke l’état (l’attribut book), définit les méthodes d’action (doCreateBook()) ­utilisées par une page et gère la navigation (return "listBooks.xhtml"). Modèle d’un bean géré Les beans gérés sont des classes Java prises en charge par la FacesServlet. Les composants de l’interface utilisateur sont liés aux propriétés du bean (backing bean) et © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves 392 Java EE 6 et GlassFish 3 peuvent invoquer des méthodes d’action. Un bean géré doit respecter les contraintes suivantes : ■■ La classe doit être annotée par @javax.faces.model.ManagedBean ou son équivalent dans le descripteur de déploiement XML faces-config.xml. ■■ La classe doit avoir une portée (qui vaut par défaut @RequestScoped). ■■ La classe doit être publique et non finale ni abstraite. ■■ La classe doit fournir un constructeur public sans paramètre qui sera utilisé par le conteneur pour créer les instances. ■■ La classe ne doit pas définir de méthode finalize(). ■■ Pour être liés à un composant, les attributs doivent avoir des getters et des setters publics. Bien qu’un bean géré puisse être un simple POJO annoté, sa configuration peut être personnalisée grâce aux éléments de @ManagedBean et @ManagedProperty (ou leurs équivalents XML). @ManagedBean La présence de l’annotation @javax.faces.model.ManagedBean sur une classe l’enregistre automatiquement comme un bean géré. La Figure 12.4 présente l’API de cette annotation, dont tous les éléments sont facultatifs. Listing 12.4 : API de l’annotation ManagedBean @Target(TYPE) @Retention(RUNTIME) public @interface ManagedBean { String name() default ""; boolean eager() default false; } L’élément name indique le nom du bean géré (par défaut, ce nom est celui de la classe commençant par une minuscule). Si l’élément eager vaut true, le bean géré est instancié dès le démarrage de l’application. Les composants de l’interface utilisateur étant liés aux propriétés d’un bean géré, changer son nom par défaut a des répercussions sur la façon d’appeler une propriété ou une méthode. Le code du Listing 12.5, par exemple, renomme le bean géré Book– Controller en myManagedBean. © 2010 Pearson Education France – Java EE 6 et GlassFish 3 – Antonio Goncalves