Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

Cet article est une reprise de l'article Un projet JSF/Facelets avec Ant et Eclipse que j'avais précédemment publié sur http://wiki.esicom-st-malo.fr

JavaServer Faces (JSF) est une technologie de présentation standard JEE pour la création des interfaces utilisateur dans les applications Web. Facelets est une technologie OpenSource qui permet d'utiliser le langage XHTML pour définir les pages JSF. Dans ce tutoriel, nous allons voir comment créer une petite application JSF/Facelets avec Ant et Eclipse.

Objectifs A la fin de ce tutoriel, vous saurez:
  • Créer un projet JSF/Facelets dans Eclipse,
  • Configurer un managed-bean,
  • Accéder aux propriétés d'un bean à partir d'une page JSF.
Prérequis
Moyens

Créer le projet

Dans Eclipse

Pour ce projet nous allons utiliser l'IDE Eclipse. Eclipse nous servira pour l'édition des fichiers et la compilation. La création de l'archive de l'application (.war) sera laissé à la charge de Ant.

Si ce n'est pas déjà fait, lancez Eclipse et effectuez les opérations suivantes:

  1. Créez un nouveau projet Java: "new Java project" (dans la suite de ce tutoriel, le nom du projet sera supposé être MonProjet),
  2. Créez un nouveau répertoire appelé WebContent à la racine du projet,
  3. Créez le sous-répertoire WebContent/WEB-INF,
  4. Dans les propriétés du projet: JavaBuildPath > Sources > Default output folder: "MonProjet/classes"
  5. Valider tout

Ajouter le buildfile Ant

  1. Dans votre projet, choisissez "New File"
  2. Créez le fichier build.xml
  3. Recopiez le contenu du buildfile ci-dessous:
<?xml version="1.0" encoding="UTF-8" ?>
<project name="MyServlet" default="compile">
    <property name="project.name" value="myfaceapp" />
    <property name="project.src.dir" value="src" />
    <property name="project.web.dir" value="WebContent" />
    <property name="project.war.file" value="${project.name}.war" />
 
    <property name="JBOSS_HOME" value="../../jboss" />
    <path id="JBoss.libraryclasspath">
        <pathelement location="${JBOSS_HOME}/server/default/lib/servlet-api.jar"/>
    </path>
 
    <target name="init">
        <mkdir dir="${project.src.dir}" />
        <mkdir dir="${project.web.dir}" />
        <mkdir dir="${project.web.dir}/WEB-INF" />
        <mkdir dir="${project.web.dir}/WEB-INF/classes" />
        <mkdir dir="${project.web.dir}/WEB-INF/lib" />
    </target>
 
    <target name="clean">
        <delete dir="${project.web.dir}/WEB-INF/classes"/>
    </target>
 
    <target name="compile" depends="init" unless="eclipse.running">
        <javac srcdir="${project.src.dir}"
               destdir="${project.web.dir}/WEB-INF/classes">
            <classpath refid="JBoss.libraryclasspath" />
        </javac>
    </target>
    <target name="copyclasses" depends="init" if="eclipse.running">
        <copy todir="${project.web.dir}/WEB-INF/classes">
            <fileset dir="classes">
                <include name="**/*.class"/>
            </fileset>
        </copy>
    </target>
 
    <target name="build" depends="compile,copyclasses">
    </target>
 
    <target name="war" depends="build">
        <jar destfile="${project.war.file}" basedir="${project.web.dir}" />
    </target>
</project>

Allez maintenant dans les propriétés du projet: Builders > New builder > Ant builder. Configurer "Manual build" pour construire la cible "build".

Note:

Eclipse prendra en charge automatiquement la compilation. Pour construire l'application Web, vous devrez par contre sélectionner le projet, aller dans le menu "Project" et choisir "Build" project

Une première page JSF/Facelets

Créer le document

Créez le fichier WebContent/index.xhtml:

<!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:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html">
<head><title>Hello</title></head>
<body>
		<h1>Hello world</h1>
</body>
</html>

POX

Comme vous le remarquez, la page Facelets est une page XHTML tout ce qu'il y a de plus classique (Plain Old XHTML).

Ajouter le descripteur de déploiement

Le descripteur de déploiement est un fichier XML qui permet au serveur d'application de savoir comment doit être configurée l'application. Ici, nous allons utiliser ce fichier pour:

Créez le fichier WebContent/WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" >
 
   <!-- JSF -->
   <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>
 
   <context-param>
      <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
      <param-value>.xhtml</param-value>
   </context-param>
 
   <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.faces</url-pattern>
   </servlet-mapping>
 
   <security-constraint> 
       <display-name>Restrict raw XHTML Documents</display-name>
       <web-resource-collection>
           <web-resource-name>XHTML</web-resource-name>
           <url-pattern>*.xhtml</url-pattern>
       </web-resource-collection>
       <auth-constraint/>
   </security-constraint>
</web-app>

Ajouter le fichier de configuration de JSF

JSF possède son propre fichier de configuration pour les réglages spécifiques à cette technologie. C'est dans ce fichier que nous allons dire que nous souhaitons utiliser la bibliothèque Facelets pour traiter les pages.

Créez le fichier WebContent/WEB-INF/faces-config.xml:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              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_1_2.xsd">
   	<application>
    	<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
   	</application>
 
</faces-config>

Ajouter la bibliothèque Facelets

Maintenant que nous avons dit que nous utilisons Facelets, il faut penser à copier cette bibliothèque dans notre projet:

  1. Créez le répertoire WebContent/WEB-INF/lib s'il n'existe pas déjà.
  2. Ajouter la bibliothèque jsf-facelets.jar au répertoire WEB-INF/lib:
sh$ cp /path/to/jsf-facelets.jar WebContent/WEB-INF/lib

Arborescence du projet

Au final, votre projet doit contenir l'arborescence suivante:

./src
./classes
./build.xml
./WebContent
./WebContent/WEB-INF
./WebContent/WEB-INF/classes
./WebContent/WEB-INF/faces-config.xml
./WebContent/WEB-INF/web.xml
./WebContent/WEB-INF/lib
./WebContent/WEB-INF/lib/jsf-facelets.jar
./WebContent/index.xhtml

Tester

Ne reste plus qu'à construire l'application -- puis à la déployer:

sh$ ant war
sh$ cp -f myfaceapp.war ${JBOSS_HOME}/server/default/deploy/

Pour testez, chargez votre page à partir d'un client http (un navigateur, par ex.):

sh$ firefox http://localhost:8080/myfaceapp/index.faces

Ajouter du contenu actif

Pour l'instant notre page est statique, et l'utilisation de JSF/Facelets n'apporte rien par rapport à du bon vieux XHTML. Nous allons maintenant ajouter du contenu actif pour rendre notre page dynamique.

Nous allons transformer notre page en application de traduction: l'utilisateur va saisir un mot en français, et l'application va lui présenter la traduction en anglais.

Tout d'abord, il faut savoir qu'une page JSP peut accéder à des objets Java gérés par le serveur d'application. Ces objets sont appelés des managed-beans et doivent respecter les spécifications des JavaBeans.

Notre application utilisera un managed-bean Dictionary capable d'associer à un mot en français (la clé) le mot correspondant en anglais (la valeur). Il s'agira d'un simple wrapper autour de la classe de collection Map. On pourrait également envisager d'associer cet objet à une base de données par exemple en récupérant une connexion via JNDI. Mais ceci sera laissé à titre d'exercice.

  1. Créer un nouveau package fr.esicom.demo dans le répertoire src
  2. Créer la classe Dictionary dans ce package. Saisissez le fichier src/fr/esicom/demo/Dictionary.java ci dessous:
package fr.esicom.demo;
 
import java.util.*;
 
 
public class Dictionary {
    Map<String, String>		map;
    String                      key;
 
    public Dictionary() {
    	key = "bienvenue";
 
    	map = new HashMap<String,String>();
    	map.put("bienvenue", "welcome");
    	map.put("bonjour", "hello");
    	map.put("oui", "yes");
    	map.put("non", "no");
    }
 
    public String getValue() {
	return map.containsKey(key) ? map.get(key) : "???";
    }
 
    public void setKey(String key) {
	this.key = key;
    }
 
    public String getKey() {
	return key;
    }
}

Maintenant que nous avons défini notre classe, il faut informer le serveur d'application qu'il doit instancier des objets de cette classe, et les mettre à la disposition de notre page JSF. Cela se fait dans le fichier faces-config.xml en ajoutant l'élément managed-bean:

<!-- ... -->
	<managed-bean>
		<managed-bean-name>dictionary</managed-bean-name>
		<managed-bean-class>fr.esicom.demo.Dictionary</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
	</managed-bean>
<!-- ... -->

Managed bean scope: session?

Remarquez la ligne:

<managed-bean-scope>session</managed-bean-scope>

Celle-ci indique au serveur d'application qu'il doit gérer une instance de l'objet en question pour chaque session. Ainsi, un client utilisera toujours le même objet pour chaque requête (tant que la session n'est pas terminée). Par contre, deux clients différents utiliseront chacun une instance différente de la classe fr.esicom.demo.Dictionary.

Les autres portées possibles sont:

  • application: tous les clients de l'application utilisent le même bean
  • request: un nouveau bean est utilisé pour chaque requête

Enfin, il faut bien entendu modifier notre page pour présenter:

<!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:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html">
<head><title>Hello</title></head>
 
<body>
    <h:form id="login">
	<h1>Hello world</h1>
	<p><h:inputText value="#{dictionary.key}" /></p>
	<p><h:outputText value="#{dictionary.value}" /></p>
    </h:form>
</body>
</html>

Les expressions de la forme value="#{dictionary.key}" indiquent à l'interpréteur JSF que la valeur est associée à une propriété d'un objet (ici, la propriété key de l'objet connu dans l'application sous le nom dictionary). L'objet référencé doit fournir un getter (respectivement, un setter) pour chaque propriété accessible en lecture (respectivement en écriture). Conformément aux spécifications JavaBeans.

Le getter est automatiquement invoqué par JSF lorsque l'élément est rendu. Le setter est invoqué pour chaque valeur saisie dans un formulaire lorsque l'utilisateur poste une réponse. Toute cette plomberie est gérée en transparence par JSF côté serveur.

Ne reste plus qu'à compiler et déployer l'application -- puis à tester:

sh$ ant war && cp -f myfaceapp.war ${JBOSS_HOME}/server/default/deploy/
sh$ firefox http://localhost:8080/myfaceapp/index.faces

Note:

JSF définit de nombreux éléments d'interface utilisateur. Pas seulement des champs de texte:

  • inputSecret pour la saisie de mot de passe
  • commandButton un bouton à cliquer
  • graphicImage une image
  • selectOneMenu une menu avec un choix unique
  • selectBooleanCheckbox une case à cocher
  • etc.

On se reportera aux documents de référence pour en connaître la liste complète et les détails de fonctionnement.

POX

Jusqu'à présent, nous avons utiliser des éléments JSF pour définir le contenu dynamique de notre page. Cela fonctionne très bien, mais a tout de même deux inconvénients:

  1. tout d'abord, l'auteur des pages doit comprendre ces éléments. Cela semble évident, et peu problématique si l'auteur est un développeur JEE. C'est plus délicat si l'auteur des pages est un designer web qui maitrise XHTML et CSS, mais pas JSF...
  2. ensuite, le cycle de développement graphique des pages est alourdi par la nécessité de déployer les pages JSF sur un serveur d'application compatible JSF. A nouveau pour un designer web, il serait sans doute plus simple de pouvoir visualiser les pages en les chargeant directement - ou via un simple serveur web - dans son navigateur.

Facelets résout ces deux problèmes en permettant d'utiliser du XHTML (POX - Plain Old XHTML) pour définir des pages JSF. Les éléments XHTML standards étant automatiquement remplacés par leurs contreparties JSF lors du traitement de la page.

Ainsi, dans notre exemple nous allons pouvoir utiliser:

<!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:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html">
<head><title>Hello</title></head>
 
<body>
    <form jsfc="h:form" id="login">
		<h1>Hello world with POX</h1>
		<p><input type="text" jsfc="h:inputText" value="#{dictionary.key}" /></p>
		<p>#{dictionary.value}</p>
    </form>
</body>
</html>

Remarque:

Notez l'attribut jsfc des éléments form et input. Il ne s'agit pas d'un attribut XHTML, mais du moyen utilisé par Facelets pour savoir par quel élément JSF l'élément XHTML doit être remplacé.

Et voilà: notre application fonctionne toujours de la même manière, mais en plus, le document brut peut être chargé directement dans un navigateur web (ou un éditeur HTML) ce qui facilite grandement le travail du designer web.

A vous de jouer

Cette rapide présentation de JSF et de Facelets offre de nombreuses possibilités d'expérimentation. Par exemple, vous pouvez:

Ressources