Technologies

JSF2 – JavaServer Faces

Publié le : Auteur: Tchy MOUA Laisser un commentaire
JSF2

Introduction

Quel Framework Web choisir lorsqu’on commence un nouveau projet Web en Java ? Comme vous le savez certainement, il en existe une panoplie. Eh bien, je vais essayer de vous rendre encore plus indécis sur ce choix en vous présentant Java Server Faces (JSF). Je plaisante, à la fin de cet article je préconiserai des types applications sur lequel JSF irait comme un gant.

JSF d’où ça sort ? JSF est une spécification. La dernière version de JSF, qui est la 2.2, fait l’objet de la JSR 344. Il est donc possible de tomber sur plusieurs implémentations de JSF, notamment celle d’Oracle nommée « Mojarra » et celle d’Apache nommée « Myfaces ».

JSF se base sur le pattern MVC, on retrouve dans un projet JSF une partie vue qui se chargera du rendue de la page envoyée au client, une partie contrôle qui vérifiera la cohérence des données saisies et une partie modèle qui s’occupera des transactions avec les données.

Comment ça marche ?

JSF s’articule autour de son fichier de configuration « faces-config.xml ». A l’intérieur, on retrouve la déclaration des contrôleurs, des validateurs et celles des règles de navigation.

Depuis JSF 2, la plupart de ces déclarations sont omîtes et remplacer par des annotations dans les classes.

Ci-dessous, le cycle de vie de JSF :

Lorsque JSF réceptionne une requête, les données envoyées passent par une phase de validation avant d’affecter le model. Une fois les valeurs affectés au model, le contrôle effectue ses traitements et restitue la vue en réponse selon les règles de navigation définies.

Configuration d’un projet JSF

A partir d’un projet web, il est très facile de passer à un projet JSF.

  1. Importer les librairies
  2. Ajouter un fichier « faces-config.xml » dans le dossier WEB-INF
  3. Déclarer la servlet JSF et le mapping de cette dernière dans le « web.xml »

Pour éviter de faire manuellement les étapes 2 et 3 ci-dessus, Eclipse propose un facet afin de réaliser ces manipulations automatiquement. En ce qui concerne l’étape 1, il est possible de trouver la librairie pour myfaces ici (http://myfaces.apache.org/core22/index.html) ou pour ceux qui veulent passer par Maven, insérer dans le « pom.xml » :

		<dependency>
			<groupId>org.apache.myfaces.core</groupId>
			<artifactId>myfaces-impl</artifactId>
			<version>2.2.0</version>
		</dependency>

web.xml

Dans le « web.xml », comme dit ci-dessus, on retrouve la déclaration de la servlet JSF ainsi que son mapping.

<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>

Concernant notre exemple, le Framework se déclenchera seulement pour les URLs contenant « faces ». Il est possible de travailler avec des patterns d’URL différents, par exemple :

<url-pattern>*.jsf</url-pattern>

Ou encore, pour plus de fun :

<url-pattern>*.javateamsodifrance</url-pattern>

NB : dans le cas où on travaille avec le pattern « *.jsf », lorsqu’on souhaite accéder à la page « index.xhtml » on devra appeler dans l’URL « index.jsf »

Le MVC chez JSF

Modèle

La partie modèle constitue les classes représentants vos entités à enregistrer ainsi que les méthodes retraçant toutes vos règles de gestion. JSF ne propose rien de spécifique à ça, mais bien évidemment vous êtes libre d’utiliser n’importe quelle technologie de persistance que vous souhaitez (pour ma part, petit clin d’œil aux l’EJB).

Contrôleur

Les contrôleurs sont appelés en JSF des « managed-bean ». Les managed-bean sont des POJO et lors de la création d’un managed-bean il y a 2 éléments à définir : son nom et son scope.

@ManagedBean(name="helloWorldManagedBean")
@RequestScoped
public class HelloWorldManagedBean {

       private String name;

       public String sayHelloAction()
       {
             return "/helloworld/helloworld.xhtml";
       }

        public String getName() {
             return name;
       }

       public void setName(String name) {
             this.name = name;
       }
}

Par défaut, si aucun nom est donné pour le managed-bean, son nom sera le nom de la classe avec la première lettre en minuscule ce qui donne par exemple « helloWorldManagedBean » pour la classe « HelloWorldManagedBean ». Le nom servira d’identificateur lors de son appel coté vue.

Par défaut, si aucun scope n’est défini, le scope par défaut du managed-bean est « request ». Le scope définie la longévité de l’instance du managed-bean avant d’être détruite. Dans notre cas, l’instance de notre managed-bean est gardée seulement le temps d’une requête cliente.

Les scopes

Il y a 5 valeurs de scope possible :

  • Request (@RequestScoped) : l’instance du managed-bean est gardé le temps d’une requête
  • Session (@SessionScoped) : l’instance du managed-bean est gardé le temps d’une session (correspond à une session web, expire au bout d’un certain temps)
  • Vue (@ViewScoped) : l’instance du managed-bean est gardé tant que le client reste sur la même page
  • Application (@ApplicationScoped) : l’instance du managed-bean est gardé tout le long du déploiement de l’application
  • None (@NoneScoped) : l’instance est systématiquement détruite est reconstruite à chaque appel

Pour ma part, les scopes les plus utilisées sont « request », « session » et « vue ».

Pour ceux qui sont intrigués par la scope « vue », je vais apporter quelques précisions.

De nos jours, il est fréquent qu’une même page fasse l’objet de plusieurs requêtes vers le serveur, notamment grâce à l’Ajax. Dans ce cas, il est obligatoire de travailler avec un managed-bean en scope « vue » sous peine de voir l’instance de son managed-bean réinitialisé à chaque requête.

Les actions

Les actions sont des méthodes au sein des managed-bean. Leur rôle est d’effectuer un traitement (contrôle et appel à la couche métier) et de rediriger vers la bonne page selon le traitement réalisé.

public String sayHelloAction()
{
      return "/helloworld/helloworld.xhtml";
}

Une action doit obligatoirement retourner une chaine de caractère, qui correspond à la page vers laquelle l’utilisateur doit être dirigé.

Dans notre exemple, on dirige notre utilisateur vers la page « helloworld.xhtml ».

Les actions sont appelés lors de la soumission de formulaire coté client. Nous verrons plus bas comment ces actions sont appelées

Vue

Nous allons voir maintenant comment utiliser notre contrôleur coté vue. Pour cela, JSF2 ne travaille pas avec des JSP mais avec le gestionnaire de vue Facelet. Ce dernier utilise non pas des JSP mais des fichiers xhtml. Voici à quoi ressemble une page xhtml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

<h:head>

<title>JSF 2.0 Hello World</title>

</h:head>

<h:body>

<h3>JSF 2.0 Hello World Example - hello.xhtml</h3>

<h:form>

<h:inputText value="#{helloWorldManagedBean.name}"></h:inputText>

<h:commandButton value="Say Hello" action="#{helloWorldManagedBean.sayHelloAction}"></h:commandButton>

</h:form>

</h:body>

</html>

Les taglibs

JSF possède 3 librairies de tags disponibles qui sont :

  • Core : tag utilitaire (conversion de date, conversion de nombre, ajax, etc.)
  • HTML : tag représentant les éléments HTML
  • UI : tag templating

La déclaration des taglibs se font de la façon suivante :

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

Où  « f » et « h » sont les préfixes pour respectivement appeler les tags des librairies « core » et « html ».

Soumission d’un formulaire

Pour soumettre un formulaire, on construit notre page comme une page HTML classique, sauf qu’il faut utiliser les tags de la librairie JSF HTML. Le fait d’utiliser les balises JSF va permettre d’indiquer à JSF d’affecter la valeur du champ texte à la propriété du managed-bean lors de la soumission du formulaire.

Pour atteindre les managed-beans, JSF utilise les expressions de langages (EL) symbolisées par « #{} ». Pour l’appel d’un managed-bean on procède de la façon suivante :

« #{<nomManagedBean>.<nomPropriété>} »

Il est possible d’accéder à des propriétés à plusieurs niveaux. Par exemple, nous supposons que nous avons un managed-bean « AjoutePersonneManagedBean » avec en propriété un objet de type « Personne », lui-même composé d’un « Nom ». Il est possible d’atteindre directement le nom de la personne en procédant comme suit :

« #{ajoutePersonneManagedBean.unePersonne.prenom} »

Bien entendu, avant que la valeur du champ texte soit valorisée à la propriété du managed-bean, il faut d’abord que la requête passe la phase de validation (voir cycle de vie).

La soumission du formulaire se fait grâce au composant « commandButton ».  La particularité de ce composant est l’attribut « action ». L’attribut action prend une chaine de caractère en valeur qui va lui permettre de diriger l’utilisateur vers la page souhaitée. D’où la nécessité pour la méthode appelée (l’action) de retourner une chaine de caractère (voir la partie sur les actions).

<h:commandButton value="Say Hello" action="#{helloWorldManagedBean.sayHelloAction}"></h:commandButton>

Il est possible de faire comme cela :

<h:commandButton value="Say Hello" action="/helloworld/helloworld.xhtml "></h:commandButton>

Mais évidemment, cette action va amener l’utilisateur vers la page “helloworld.xhtml” sans condition et sans contrôle.

Pour afficher la valeur saisie, il suffit de faire sur la page « helloworld.xhtml »

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

<h:head>

<title>JSF 2.0 Hello World</title>

</h:head>

<h:body>

<h3>Hello #{helloWorldManagedBean.name}</h3>

</h:body>

</html>

Petite parenthèse :

Vous pouvez profiter de cet exemple pour tester le scope des managed-bean. Si votre managed-bean est en scope « request » et que vous rafraîchissait votre page vous verrez que votre propriété « name » ne sera plus valorisé, vous vous retrouverez avec un message « Hello » au lieu de « Hello Tchy » par exemple. Par contre, si votre managed-bean est en scope « session », vous aurez le message « Hello votrenom »jusqu’à expiration de la session.

Attention : lorsque vous essayer de rafraîchir votre page, pour les plus attentifs, vous noterez que JSF a une URL de retard. J’entends par là que vous affichez la page « helloworld.xhtml » mais l’url indique toujours « index.xhtml ». Donc saisissez la bonne url à la main. N’ayez crainte ce n’est que pour un test, on ne demandera jamais à un utilisateur de saisir lui-même l’url.

Validation sur un champ

Mais si je veux rendre le champ texte obligatoire, comment je fais ? Sur notre champ texte, il suffit de rajouter les mots « required=true ».

<h:inputText label="champs nom" value="#{helloWorldManagedBean.name}" required="true" ></h:inputText>

Vous retrouverez l’attribut « required » sur tous les composants HTML de type « input » (champs texte, zone de texte, etc.) et de sélection (liste déroulante, boutons radio, etc.)

Ce n’est pas tout, JSF propose encore d’autres outils de validation sur les champs. On peut les retrouver dans la librairie de tag « core ». Pour les utiliser, faites comme suit :

<h:inputText label="champs nom" value="#{helloWorldManagedBean.name}" required="true" >

<f:validateLength maximum="15" minimum="3"></f:validateLength>

</h:inputText>

Pour voir l’affichage des messages d’erreur de validation sur les champs, il faut ajouter une balise « h:messages ». Au final votre page doit donner cela :

<h3>JSF 2.0 Hello World Example</h3>

<h:form>

<h:messages></h:messages>

<h:inputText label="champs nom" value="#{helloWorldManagedBean.name}" required="true" >

<f:validateLength maximum="15" minimum="3"></f:validateLength>

</h:inputText>

<h:commandButton value="Say Hello"                    action="#{helloWorldManagedBean.sayHelloAction}"></h:commandButton>

</h:form>

N’oubliez pas de renseigner l’attribut « label » de l’inputText sinon vous aurez du texte étrange dans votre message « j_id_8 :j_id_a:Erreur de validation ».

Pour voir les autres possibilités que JSF offre en terme de validation de champs, servez-vous de l’auto complétion en tapant « <f :validate ».

Satisfait ? Non ? Pourquoi ? Parce que vous voulez que la saisie correspond à une adresse email qui n’est pas yopmail et que la taille maximale doit être de 25 caractères  et qu’elle ne contient pas de caractères spéciaux et que… et que… et que…

Eh bien, pour cela, on va voir comment créer un validateur custom.

Pour créer son propre validateur, nous allons devoir passer par une classe qui implémente l’interface « Validator » du package « javax.faces.validator ».

@FacesValidator(value = "emailValidator")
public class EmailValidator implements Validator {

@Override
public void validate(FacesContext arg0, UIComponent arg1, Object arg2)
throws ValidatorException {

//On récupère la valeur du champs sous forme de String
String valeurSaisie = (String) arg2;

//On vérifie que la valeur saisie contient bien un "@"
if(!valeurSaisie.contains("@"))

throw new ValidatorException(new FacesMessage("La saisie n'est pas une adresse mail"));

}
}

A noter sur un validateur :

  • l’annotation « @FacesValidator » et l’attribut « value », retenez bien le nom « emailValidator » il servira d’identifiant pour le validateur
  • le paramètre de type « Object », qui est la valeur contenu dans le champ texte, dans notre cas le type sera forcément « String », il y a des cas où  la valeur retournée n’est pas forcément un « String »
  • le « throw new ValidatorException », lorsqu’on recense une anomalie de validation, on lance une exception

Petite remarque mais importante pour ne pas m’attirer les foudres de tout le monde, j’ai volontairement simplifié le validateur, évidemment il ne suffit pas de contrôler qu’il y a un « @ » pour certifier que la chaîne correspond à un email.

Pour l’utiliser :

<h:inputText label="champs nom" value="#{helloWorldManagedBean.name}"
required="true">
<f:validator validatorId="emailValidator" />
</h:inputText>

Librairies de composants supplémentaires

De base, les composants proposés par JSF sont limités à la représentation des balises que l’on peut retrouver en HTML. Par conséquent, les applications peuvent manquer de convivialité sans un gros coup de jQuery ou autre Framework Javascript.

Pour pallier à ce manque, il existe de nombreuses librairies de composant riches proposant par exemple : calendrier, agenda, spinner, graphique, gallerie photos…

Leur intégration au projet est très simpliste, il suffit de rajouter au projet le « .jar » et de déclarer le taglib sur la page sur laquelle on souhaite utiliser les composants.

Les composants sont pleinement intégrés à JSF, j’entends par là que pour n’importe quelle composant, il suffit de remplir les attributs par des EL pour en définir le contenu et en récupérer la valeur.

Voici une liste non exhaustive des librairies existantes :

Templating

Un site web sans template c’est possible ? Non ! On va voir ça vite fait, bien fait !

Etape 1 :

On crée un squelette de template que j’ai nommé « template.xhtml ». Qu’est-ce qu’on remarque ici ? L’utilisation de la librairie de tag « ui ». Le tag « ui :insert » va défnir les endroits dans le template où les pages filles pourront insérer leur propre code. Dans notre exemple, on va pouvoir insérer notre html entre les balises « body ». L’attribut « name » sert d’identifiant.

Petit tips : n’hésitez pas à mettre un « ui :insert » entre les balises « head », ça vous permettra d’insérer des scripts selon vos besoins dans une page fille.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">

<h:head>

<title>JSF 2.0</title>

</h:head>

<h:body>

<ui:insert name="body"></ui:insert>

</h:body>

</html>

Etape 2 :

Pour utiliser le template :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">

<ui:composition template="/template/template.xhtml">

<ui:define name="body">

<h3>JSF 2.0 Hello World Example</h3>

<h:form>

<h:messages></h:messages>

<h:inputText value="#{helloWorldManagedBean.name}"

required="true">

<f:validator validatorId="emailValidator" />

</h:inputText>

<h:commandButton value="Say Hello"

action="#{helloWorldManagedBean.sayHelloAction}"></h:commandButton>

</h:form>

</ui:define>

</ui:composition>

</html>

Lors du rendu de la page, seul le code entre les balise « ui :composition » seront interprété. Dans cette balise on définit le template à utiliser. Ensuite le « ui :define » sert à définir les différents emplacement définit dans le template.

Ce qui peut être troublant est l’apparition du tag « html » dans le template et dans la page fille. Mais ayez confiance elle apparaîtra qu’une seul fois.

Conclusion

JSF est un framework web complet et très puissant. Il s’inscrit dans la mentalité des développeurs, à savoir réutilisabilité et maintenabilité.

Les librairies de composants riches permettent d’obtenir rapidement des applications conviviales, et leur facilité d’utilisation permet une grande productivité.

JSF est une grosse machine, pour l’utiliser à son plein potentiel, il faut bien connaître tous les outils et son cycle de vie ce qui peut être décourageant.

JSF vise les grosses applications. Par ces outils (validateur, managed-bean, converter, création de composant custom, librairie de composants riches), la moindre ligne de code pourra être isolé pour être réutilisé.

Par contre, JSF n’est pas adapté pour les applications totalement en AJAX. JSF atteindra rapidement ces limites.

J’ai réalisé mon exemple sur eclipse avec un tomcat6.

Pour aller plus loin

Nous avons vu les notions nécessaires pour afficher un « Hello world ». Mais JSF est loin de s’arrêter à ces notions. Si vous voulez aller plus loin, je vous conseille de voir les notions suivantes :

  • FacesMessage
  • Converter
  • Attribut « rendered » sur les tags HTML JSF
  • Tag « f :ajax »
  • FacesContext
  • Internationalisation

Pour aller encore plus loin

Une application JSF peut faire l’objet d’un site web ouvert au grand public. Par conséquent, certains détails pour le référencement est important comme l’apparence de l’URL. En référencement, il est important de pouvoir modeler l’URL (URL Rewriting). Je vous conseille pour cela PrettyFaces (http://ocpsoft.org/prettyfaces/).