TP2 2011 EJB 3.1 / JSF2

De $1

Version de 11:43, 7 Mai 2024

cette version.

Revenir à liste des archives.

Voir la version actuelle

Introduction

Dans cette séance :

  • Exercices pour comprendre le modèle de navigation de JSF (je reprends ici une partie d'un TP de R.Grin, original sur : http://deptinfo.unice.fr/~grin/mescours/web-entreprise/tp/tpprg/),
  • Etude d'une correction du TP1 qui corrige les problèmes évoqués à la fin du TP1, notamment en mettant en oeuvre un Backing Bean de portée View.
  • Début du TP long à rendre sur un gestionnaire de comptes bancaires. Pour les nouveaux n'ayant pas suivi ce cours l'an dernier, vous verrez notamment comment créer une base de données à partir de zéro pour l'intégrer dans votre application.

Première partie : étude du modèle de navigation PRG (Post Redirect Get)

Objectifs de cette partie :

  1. Présenter les problèmes du POST utilisé par défaut par JSF, vus dans le TP1,
  2. Présenter le modèle PRG qui permet d'éviter la double soumission des formulaires et de pouvoir garder un marque-page des pages affichées.

Important : dans ce TP vous utiliserez des backing bean de portée "Requête". Il faut toujours essayer de limiter au maximum la portée des beans pour éviter l'encombrement de la mémoire (ce qui peut être un problème pour les sites Web très fréquentés). Evidemment dans certains cas, la portée "Session" est la plus indiquée (par ex pour un backing bean qui gère les connexions par login/password).

Exemple 1 : analyse d'un post tout simple

Nous allons étudier une application qui contient 2 pages JSF qui se référencent :

  • index.xhtml (point d'entrée de l'application Web) qui contient un formulaire avec champ de saisie d'un entier ; la soumission du formulaire passe la main à page2.xhtml,
  • page2.xhtml qui affiche les 5 nombres qui suivent le nombre saisi et qui contient un lien pour revenir vers la page index.html

Vous créerez un nouveau projet de type Web Application. Utilisez simplement le menu "new/JSF Page" pour créer les pages. 

Voici le code des deux pages en question :

Index.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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

    <h:head>
        <title>Test 1</title>
    </h:head>
    <h:body>


     <h:form>
        <h:outputLabel value="Entrez un nombre" for="nombre"/>

        <h:inputText id="nombre" value="#{bean.nombre}"/>

        <h:commandButton value="Valider" action="page2"/>

      </h:form>

    </h:body>
</html>

page2.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Afficher des nombres</title>
    </h:head>
    <h:body>
        Les nombres : <br/>
        <ui:repeat value="#{bean.nombresSuivants}" var="n">

            #{n},
        </ui:repeat>
        <h:form>
        <h:commandLink action="index" value="Saisir un autre nombre"/>
        </h:form>
    </h:body>
</html>

Ces pages utilisent un Backing Bean (son nom est "bean") pour stocker la propriété correspondant au champs de formulaire "nombre". C'est la ligne suivante dans index.html qui correspond à la propriété :

<h:inputText id="nombre" value="#{bean.nombre}"/>

dcsdxfd

/div[2]/div/pre[4], line 1, column 1: EOF expected

Voici donc le code du backing bean (que nous avons, dans cet exemple, mis dans le package "jsf") :

Bean.java :

package jsf;

import java.util.ArrayList;
import java.util.List;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;

@Named(value = "bean")
@RequestScoped
public class Bean {

    private int nombre;

    public int getNombre() {
        return nombre;
    }

    public void setNombre(int nombre) {
        this.nombre = nombre;
    }

    public List<Integer> getNombresSuivants() {
        int nb = 5;
        ArrayList<Integer> t = new ArrayList(nb);
        for (int i = nombre; i < nombre + nb; i++) {
            t.add(i);
        }
        return t;
    }
}

Regardez déjà comment cet exemple s'articule. Notez dans les pages JSF l'utilisation de <ui:repeat ... /> qui correspond au tag JSTL vu l'an dernier <c:foreach... />. Notez que la collection sur laquelle on itère, donnée par :

/div[2]/div/pre[5], line 1, column 7: ":" expected

correspond encore à un accès à une propriété, la propriété "nombresSuivants" qui existe puisqu'une méthode getNombresSuivants() existe. Rappelons que ce sont les getters et setters qui définissent une propriété et non pas l'existence d'une variable d'instance dans la classe. En particulier ici on a pas de variable d'instance.

Exécutez cet exemple :

  • Testez en entrant autre chose qu'un nombre entier (un message doit s'afficher si vous avez bien écrit votre application),
  • Essayez de faire afficher au début dans la zone de saisie le nombre entré précédemment quand vous saisissez un nouveau nombre.

Remarquez-vous des anomalies quant à l'URL affiché par le navigateur ?

Rechargez la page qui affiche les 5 nombres. Que fait le navigateur ? Expliquez.

Est-ce possible de mettre un signet sur la page 2 pour un certain nombre. Par exemple 5 ; la page 2 affichera les nombres 6 à 10 qui sont censés représenter une information importante sur le nombre 5...

Et oui, cela ne marche pas ! Ce TP étudie un modèle, appelé PRG, pour ne plus avoir ce genre de problème.

Exemple 2 : utilisation de la redirection (modèle PRG)

Dans cet exemple nous allons faire une implémentation du modèle PRG vu en cours : la page résultat est affiché par une redirection (GET) après le POST.

On va commencer par régler le problème de l'URL. Pour cela, on va changer le comportement par défaut de JSF qui utilise le "forward" pour faire afficher les pages JSF (voir cours). Pour utiliser une redirection il suffit d'ajouter aux URLs dans les pages JSF "faces-redirect=true", regardez le passage du cours à ce sujet.

  • Créez deux nouvelles pages JSF, appelez-les index_2.xhtml et page2_2.xhtml.
  • Recopiez le contenu des pages index.xhtml et page2.xhtml dans les nouvelles pages, ainsi vous pourrez comparer.
  • Modifiez ces pages pour que JSF utilise une redirection à la place d'un forward : lorsqu'on va de index_2.xhtml à page2_2.xhtml et vice-versa.

Pour tester, lancez l'application et, à la main, tapez le bon URL pour faire afficher la page JSF index_2.xhtml (note vous pouvez aussi éditer la page index.html et vous faire un petit menu avec des liens vers les différentes pages index.xhtml, index2.xhtml etc...).

Est-ce que les URL sont corrects maintenant ?

Rechargez la page qui affiche les nombres. Avez-vous toujours le problème de l'exercice précédent ?

Est-ce que les bons nombres sont bien affichés ? Expliquez.

Exemple 3 : utilisation de la mémoire flash

Dans cet exemple nous allons utiliser la "mémoire flash" pour faire afficher les bons nombres dans la 2ème page.

Pour ne pas toucher aux pages déjà écrites, mettez le suffixe "_3" aux nouvelles pages que vous allez écrire.

Principe : dans le formulaire de la page index3.xhtml nous allons associer au champs input nom pas une propriété d'un backing bean (comme #{bean.nombre}) mais nous allons positionner cette variable dans une mémoire temporaire qui va durer un peu plus que la durée de la requête : la fameuse "mémoire flash". Au lieu de #{bean.nombre} nous allons mettre #{flash.nombre} :

index3.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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>

     <h:form>
        <h:outputLabel value="Entrez un nombre" for="nombre"/>

        <h:inputText id="nombre" value="#{flash.nombre}"/>

        <h:commandButton value="Valider" action="page2_3?faces-redirect=true"/>

      </h:form>
    </h:body>
</html>

Lorsque le formulaire est validé, on appelle bien la page page2_3.xhtml qui est comme ceci (seule différence on ne reférence pas #{bean.nombre} mais #{bean3.nombre} qui est la nouvelle version du backing bean, source un peu plus loin :

page2_3.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Les nombres : <br/>
        <ui:repeat value="#{bean3.nombresSuivants}" var="n">
            #{n},
        </ui:repeat>
        <h:form>
        <h:commandLink action="index_3?faces-redirect=true" value="Saisir un autre nombre"/>
        </h:form>
    </h:body>
</html>

Vous aurez besoin de modifier le backing bean. Pour ne pas perdre le précédent, créez un nouveau bean "Bean3.java" pour cet exercice :

package jsf;

import java.util.ArrayList;
import java.util.List;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;

@Named(value = "bean3")
@RequestScoped
public class Bean3 {

    private int nombre;
  
    public int getNombre() {
        return nombre;
    }

   
    public void setNombre(int nombre) {
        this.nombre = nombre;
    }

    public List<Integer> getNombresSuivants() {
        int nb = 5;
        // UTILISATION DE LA MEMOIRE FLASH
        String s = (String)FacesContext.getCurrentInstance()
                .getExternalContext().getFlash().get("nombre");
        int n = 0;
        if (s != null) {
          n = Integer.parseInt(s);
        }

        ArrayList<Integer> t = new ArrayList(nb);
        for (int i = n; i < n + nb; i++) {
            t.add(i);
        }
        return t;
    }
}

Les lignes qui suivent le commentaire "UTILISATION DE LA MEMOIRE FLASH" indiquent juste qu'on va récupérer la valeur d'une propriété "nombre" dans cet objet "magique" qu'est "la mémoire flash", une mémoire qui dure un peu plus que la durée d'une requête.

Exécutez l'application !

  • Lorsque tout marche bien, pouvez-vous garder un marque-page pour faire à nouveau afficher les nombres qui suivent un certain nombre, par exemple 25 ?
  • Testez pour voir (saisissez d'abord un autre nombre pour ne pas vous faire tromper par le cache du navigateur).

La mémoire flash peut être utile pour conserver facilement une information dans la requête suivante (et on peut même prolonger encore plus avec "keep" ; voir le cours).

Nous allons maintenant voir une méthode un peu plus élégante pour arriver à nos fins tout en résolvant le problème du bookmark/marque page. 

Exemple 4 : utilisation des paramètres de vue

Dans cet exercice nous allons utiliser une toute autre façon pour résoudre le problème du passage de la valeur tout en permettant l'affichage synchronisé de l'URL dans le navigateur.

Nous allons aussi régler le dernier problème : il sera possible de conserver les informations dans un marque-page du navigateur.

Reprenez le code de l'exercice sur la redirection (nous n'allons plus utiliser la mémoire flash dans cet exercice). Mettez le suffixe 4 aux pages utilisées par cet exercice : index4.xhtml et page2_4.xhtml

Dans la page index4.xhtml nous allons indiquer :

  • Que bean.nombre est maintenant un "paramètre de vue", il sera ainsi conservé dans une mémoire pendant la redirection, ceci se fait à l'aide des tags <f:metadata> et <f:viewParam ... />,
  • On va ajouter un paramètre http dans l'action du formulaire pour indiquer que l'on veut que les paramètres de vue soient ajoutés à l'URL, ainsi la page qui affiche les nombres sera bookmarkables/ajoutable dans le marque page (paramètre includeViewParams=true)

index4.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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <f:metadata>
        <f:viewParam name="nombre" value="#{bean.nombre}"/>
    </f:metadata>
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:outputLabel value="Entrez un nombre" for="nombre"/>
            <h:inputText id="nombre" value="#{bean.nombre}"/>

            <h:commandButton value="Valider" action="page2_4?faces-redirect=true&amp;includeViewParams=true"/>
        </h:form>
    </h:body>

</html>

Dans page4.xhtml qui affiche les 5 nombres on ajoutera un paramètre de vue pour récupérer le nombre saisi par l'utilisateur :

page2_4.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <f:metadata>
        <f:viewParam name="nombre" value="#{bean.nombre}"/>
    </f:metadata>
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>

        Les nombres : <br/>
        <ui:repeat value="#{bean.nombresSuivants}" var="n">
            #{n},
        </ui:repeat>
        <h:form>
        <h:commandLink action="index_4?faces-redirect=true" value="Saisir un autre nombre"/>
        </h:form>
    </h:body>
</html>

Testez votre application. Etudiez en particulier les URL affichés par le navigateur. Est-ce possible de garder un marque-page pour garder, par exemple, les 5 nombres qui suivent le nombre 34 ?

Après avoir testé, ajoutez le fait que le nombre précédemment saisi apparaisse lorsque l'utilisateur saisit un autre nombre !

Correction des exercices sur la navigation JSF

Vous trouverez dans ce projet une correction des exercices de navigation, ainsi qu'un rappel des eplications sur les divers comportements : TP1JSFRicxhard.zip