• 10 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 03/09/2019

Mettez en place une cinématique d'écrans

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Dans le chapitre précédent, nous avons posé le squelette d'une application Struts. Utilisons maintenant ce framework afin de mettre en place une cinématique d'écrans.

Je vous propose de voir cela autour de cas assez simples :

  • afficher la liste des projets et le nom de leur responsable

  • un lien permet d'afficher le détail d'un projet

  • un lien permet d'afficher le détail d'un responsable de projet

Voici rapidement ce que je vais devoir faire :

  • Créer 2 actions :

    • une action de gestion des projets : GestionProjetAction. Cette action aura deux points d'entrée :

      • un pour afficher la liste des projets ;

      • un autre pour afficher le détail d'un projet.

    • une action de gestion des utilisateurs : GestionUtilisateurAction.

  • Créer les JSP pour les 3 vues :

    • liste des projets

    • détail d'un projet

    • détail d'un utilisateur

  • Configurer le mapping Struts avec ces nouveaux éléments

  • Ajouter un lien vers les listes de projets sur la page d'accueil

Créer les actions

Les actions à créer ne se résument plus simplement à faire appel à une JSP comme dans le chapitre précédent. Il va falloir implémenter les actions pour rapatrier les informations nécessaires à l'affichage (liste des projets, utilisateurs...) afin de pouvoir utiliser ces dernières dans les JSP.

Dans Struts, chaque action est prise en charge par une classe. Je vais donc créer 2 classes pour implémenter les 2 actions :

  • GestionProjetAction ;

  • GestionUtilisateurAction.

La classe d'action GestionProjetAction

package org.example.demo.ticket.webapp.action;

import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

import org.example.demo.ticket.model.bean.projet.Projet;
import org.example.demo.ticket.model.exception.NotFoundException;
import org.example.demo.ticket.webapp.WebappHelper;


/**
 * Action de gestion des {@link Projet}
 */
public class GestionProjetAction extends ActionSupport {


    // ==================== Attributs ====================
    // ----- Paramètres en entrée
    private Integer id;

    // ----- Eléments en sortie
    private List<Projet> listProjet;
    private Projet projet;


    // ==================== Getters/Setters ====================
    public Integer getId() {
        return id;
    }
    public void setId(Integer pId) {
        id = pId;
    }
    public List<Projet> getListProjet() {
        return listProjet;
    }
    public Projet getProjet() {
        return projet;
    }


    // ==================== Méthodes ====================
    /**
     * Action listant les {@link Projet}
     * @return success
     */
    public String doList() {
        listProjet = WebappHelper.getManagerFactory().getProjetManager().getListProjet();
        return ActionSupport.SUCCESS;
    }


    /**
     * Action affichant les détails d'un {@link Projet}
     * @return success / error
     */
    public String doDetail() {
        if (id == null) {
            this.addActionError("Vous devez indiquer un id de projet");
        } else {
            try {
                projet = WebappHelper.getManagerFactory().getProjetManager().getProjet(id);
            } catch (NotFoundException pE) {
                this.addActionError("Projet non trouvé. ID = " + id);
            }
        }

        return (this.hasErrors()) ? ActionSupport.ERROR : ActionSupport.SUCCESS;
    }
}

Hériter d'une classe Struts ?

Bien que Struts n'impose pas de dériver d'une classe de base ou d'une interface, je fais tout de même ici hériter mes actions de la classe ActionSupport fournie par Struts. Celle-ci implémente déjà un certain nombre de mécanismes et d'éléments de base.

Les « entrées/sorties » de l'action

J'ai ensuite ajouté plusieurs attributs et leurs getter/setter :

  • id : pour récupérer, dans les paramètres de la requête HTTP (en GET ou POST, peu importe), l'identifiant du projet dont on veut afficher les détails.

  • listProjet : rempli par l'action et permettant de disposer de la liste des projets dans la JSP de liste.

  • projet : affecté également par l'action et permettant de disposer du projet dans la JSP de détail.

Les méthodes des actions

Par défaut, si rien n'est précisé dans le mapping, Struts appelle la méthode public String execute() de la classe d'action. Cette méthode est déjà implémentée dans la classe ActionSupport et renvoie simplement ActionSupport.SUCCESS. La valeur (String) renvoyée est le nom du Result à utiliser à la fin de l'action. Celui-ci est configuré dans le mapping Struts. Une action peut donc renvoyer sur plusieurs Result différents.

Ici, pour que cela soit plus clair, je n'utilise pas la méthode execute(). J'ai créé 2 méthodes pour mes 2 points d'entrée (mes 2 cas d'utilisation) :

  • doList : pour le cas d'utilisation "Afficher la liste des projets"

  • doDetail : pour le cas d'utilisation "Afficher le détail d'un projet"

Liste des projets

Ce cas d'utilisation est très simple :

  1. J'appelle le Manager de Projet pour récupérer la liste des projets et l'affecte dans l'attribut listProjet.

  2. Le Result renvoyé est ActionSupport.SUCCESS (qui vaut "success").

Détail d'un projet

Ce cas d'utilisation est un peu plus complexe :

  1. Je récupère l'identifiant du projet passé via le paramètre id de la requête HTTP et injecté automatiquement par Struts dans l'attribut de même nom id.

  2. Si l'attribut n'est pas renseigné, j'ajoute un message d'erreur.

  3. Sinon, j'appelle le Manager de Projet pour qu'il me renvoie le Projet demandé

  4. Si le projet n'est pas trouvé, j'ajoute un message d'erreur.

  5. Enfin, si je n'ai aucun message d'erreur, le Result renvoyé est ActionSupport.SUCCESS sinon c'est ActionSupport.ERROR (qui vaut "error").

La classe d'action GestionUtilisateurAction

Maintenant que vous avez compris, vous devriez être capable d'implémenter l'action GestionUtilisateurAction :

package org.example.demo.ticket.webapp.action;

import com.opensymphony.xwork2.ActionSupport;

import org.example.demo.ticket.model.bean.utilisateur.Utilisateur;
import org.example.demo.ticket.model.exception.NotFoundException;
import org.example.demo.ticket.webapp.WebappHelper;


/**
 * Action de gestion des {@link Utilisateur}
 */
public class GestionUtilistateurAction extends ActionSupport {


    // ==================== Attributs ====================
    // ----- Paramètres en entrée
    private Integer id;

    // ----- Eléments en sortie
    private Utilisateur utilisateur;


    // ==================== Getters/Setters ====================
    public Integer getId() {
        return id;
    }
    public void setId(Integer pId) {
        id = pId;
    }
    public Utilisateur getUtilisateur() {
        return utilisateur;
    }


    // ==================== Méthodes ====================
    /**
     * Action affichant les détails d'un {@link Utilisateur}
     * @return success / error
     */
    public String doDetail() {
        if (id == null) {
            this.addActionError("Vous devez indiquer un id d'utilisateur");
        } else {
            try {
                utilisateur = WebappHelper.getManagerFactory().getUtilisateurManager().getUtilisateur(id);
            } catch (NotFoundException pE) {
                this.addActionError("Utilisateur non trouvé. ID = " + id);
            }
        }

        return (this.hasErrors()) ? ActionSupport.ERROR : ActionSupport.SUCCESS;
    }
}

Mapping des Actions

Maintenant que les actions sont créées, je vais les configurer dans le mapping Struts (via le fichier struts.xml) :

<struts>
    <package name="default" extends="struts-default">
        <!-- Action par défaut -->
        <default-action-ref name="index" />

        <!-- Action "index" -->
        <action name="index">
            <result>/jsp/index.jsp</result>
        </action>

        <!-- Action listant les projets -->
        <action name="projet_list"
                class="org.example.demo.ticket.webapp.action.GestionProjetAction"
                method="doList">
            <result>/jsp/projet/list.jsp</result>
        </action>
        <!-- Action affichant le détail d'un projet -->
        <action name="projet_detail"
                class="org.example.demo.ticket.webapp.action.GestionProjetAction"
                method="doDetail">
            <result name="success">/jsp/projet/detail.jsp</result>
            <result name="error">/jsp/error.jsp</result>
        </action>

        <!-- Action affichant le détail d'un utilisateur -->
        <action name="utilisateur_detail"
                class="org.example.demo.ticket.webapp.action.GestionUtilistateurAction"
                method="doDetail">
            <result name="success">/jsp/utilisateur/detail.jsp</result>
            <result name="error">/jsp/error.jsp</result>
        </action>
    </package>
</struts>

J'ajoute 3 actions (projet_listprojet_detailutilisateur_detail). Je les associe aux classes GestionProjetAction et GestionUtilistateurAction et je précise, pour chacune, quelle méthode de la classe doit être appelée.

Pour chaque action, je définis les results. Ici, pour les actions de détail, j'ai 2 results : un affichant le détail (result"success"), un affichant une page d'erreur si le result renvoyé par la méthode de l'action est "error".

Créer les JSP pour les vues

Maintenant que les actions sont créées et le mapping configuré, je vais créer les JSP pour les vues.

Liste des projets

Pour la vue affichant la liste des projets, je crée un fichier src/main/webapp/jsp/projet/list.jsp— conformément au mapping configuré juste avant.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ticket</title>
</head>

<body>
    <h2>Liste des projets</h2>

    <ul>
        <s:iterator value="listProjet">
            <li>
                <s:a action="projet_detail">
                    <s:param name="id" value="id" />
                    <s:property value="nom"/>
                </s:a>

                - Responsable :
                <s:a action="utilisateur_detail">
                    <s:param name="id" value="responsable.id" />
                    <s:property value="responsable.prenom"/> <s:property value="responsable.nom"/>
                </s:a>
            </li>
        </s:iterator>
    </ul>
</body>
</html>

Décorticons un peu le contenu de cette JSP.

Premièrement, j'utilise une taglib fournie par Struts. Celle-ci va me permettre d'utiliser les éléments de Struts dans mes JSP et faire le lien avec les attributs des actions par exemple.

Ensuite, j'affiche la liste des projets dans une simple liste à puce (balise HTML <ul>). Je dois boucler sur la liste des projets disponibles dans l'attribut listProjet de l'action associée dans le mapping, à savoir GestionProjetAction. Pour cela, j'utilise le tag iterator de la taglib Struts, et indique comme source d'itération listProjet.

Pour chaque projet, je crée un item de liste (balise HTML <li>) contenant :

  • le nom du projet qui est aussi un lien vers l'action de détail d'un projet,

  • le nom du responsable du projet qui est aussi un lien vers l'action de détail d'un utilisateur.

Pour afficher la valeur d'un élément (objet, attribut...), j'utilise le tag property. Comme je suis à l'intérieur d'un tag iterator, Struts a ajouté en haut la value stack l'objet courant de l'itération. Ici c'est donc le projet en cours dans la boucle. Ainsi responsable.prenom pointent vers l'attribut prenom de l'attribut responsable de l'objet projet.

Pour créer un lien vers une action Struts, j'utilise le tag a et je précise l'action Struts sur laquelle le lien doit pointer grâce à l'attribut action. Par exemple, pour pointer vers l'action de détail d'un projet, je précise l'action projet_detail configurée dans le mapping. Ensuite, étant donné qu'il faut passer l'identifiant du projet en paramètre de l'action (via le paramètre d'URL id), j'utilise un tag param et deux attributs :

  • name est le nom du paramètre d'URL à ajouter (ici id),

  • value est la valeur à mettre dans le paramètre d'URL (ici l'attribut id du projet en cours dans l'itération).

Tout cela va générer un lien tel que celui-ci :

<a href="/projet_detail.action?id=5">Projet n&deg;5</a>

Détail d'un projet

Passons à la vue affichant le détail d'un projet. Je crée donc, conformément au mapping, un fichier src/main/webapp/jsp/projet/detail.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ticket</title>
</head>

<body>
    <h2>Détail du projet</h2>

    <ul>
        <li>ID : <s:property value="projet.id" /></li>
        <li>Nom : <s:property value="projet.nom" /></li>
        <li>Date création : <s:date name="projet.dateCreation" /></li>
        <li>
            Responsable :
            <s:a action="utilisateur_detail">
                <s:param name="id" value="projet.responsable.id" />
                <s:property value="projet.responsable.prenom"/> <s:property value="projet.responsable.nom"/>
            </s:a>
        </li>
        <li>Cloturé : <s:property value="projet.cloture" /></li>
    </ul>
</body>
</html>

Ici, rien de bien compliqué. J'utilise les mêmes tags et principes que dans la JSP listant les projets.

Vous pouvez cependant remarquer une petite nouveauté : le tag date. Celui-ci a le même objectif et se comporte comme le tag property sauf qu'il est dédié à l'affichage des dates. Il est, entre autres, possible de préciser le format d'affichage grâce à l'attribut format.

Détail d'un utilisateur

Là encore, la vue affichant le détail d'un utilisateur ne pose aucune difficulté. Je crée, conformément au mapping, un fichier src/main/webapp/jsp/utilisateur/detail.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ticket</title>
</head>

<body>
    <h2>Détail de l'utilisateur</h2>

    <ul>
        <li>ID : <s:property value="utilisateur.id" /></li>
        <li>Nom : <s:property value="utilisateur.nom" /></li>
        <li>Prénom : <s:property value="utilisateur.prenom" /></li>
    </ul>
</body>
</html>

La page d'erreur

Enfin, dernière vue à créer : la page d'erreur affichée par les results"error". Conformément au mapping, je crée un fichier src/main/webapp/jsp/error.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ticket</title>
</head>

<body>
    <h2>Une erreur s'est produite</h2>

    <s:actionerror />
</body>
</html>

Ici, j'ai une JSP très simple qui affiche les messages d'erreur grâce au tag actionerror

Modification de la page d'accueil

Dernier point, je modifie la page d'accueil afin d'y ajouter un lien vers la liste des projets. Là encore, j'utilise le tag a.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ticket</title>
</head>

<body>
    <h2>Bienvenue sur l'application Ticket</h2>

    <s:a action="projet_list">Liste des projets</s:a>
</body>
</html>

Améliorations

Simplification du mapping

Vous avez sans doute remarqué que les results"error" sont les mêmes entre les actions projet_detail et utilisateur_detail. En effet, j'ai pris le parti ici de créer une page d'erreur générique (jsp/error.jsp). Que ce soit en cas de projet ou d'utilisateur non trouvé, ou pour tout autre erreur, je veux afficher cette page d'erreur.

Avec Struts, il est possible de définir des results de manière globale. Au lieu de configurer le result"error" dans chaque action, je crée un global result :

<struts>
    <package name="default" extends="struts-default">
        <default-action-ref name="index" />

        <!-- Results globaux -->
        <global-results>
            <result name="error">/jsp/error.jsp</result>
        </global-results>

        <action name="index">
            <result>/jsp/index.jsp</result>
        </action>

        <action name="projet_list"
                class="org.example.demo.ticket.webapp.action.GestionProjetAction"
                method="doList">
            <result>/jsp/projet/list.jsp</result>
        </action>
        <action name="projet_detail"
                class="org.example.demo.ticket.webapp.action.GestionProjetAction"
                method="doDetail">
            <result>/jsp/projet/detail.jsp</result>
        </action>

        <action name="utilisateur_detail"
                class="org.example.demo.ticket.webapp.action.GestionUtilistateurAction"
                method="doDetail">
            <result>/jsp/utilisateur/detail.jsp</result>
        </action>
    </package>
</struts>

J'ai ajouté un global result pour les erreurs (via l'élément <global-results>) et supprimer les results"error" dans les actions de détail.

Ainsi, par défaut, dans mon package default, si une action retourne comme result"error", c'est cette JSP (/jsp/error.jsp) qui sera affichée.

Factorisation de JSP

Je vous propose encore une piste d'amélioration : factoriser le contenu des JSP.

Toutes les JSP ont un contenu commun dans la balise <head>. Pour le moment le contenu est assez limité, mais il va être étoffé par la suite, quand j'ajouterai l'importation des CSS par exemple. Afin d'éviter de repasser sur toutes les JSP lors de l'ajout d'un fichier CSS, je vais créer une JSP avec le contenu de la balise <head> et importer cette JSP dans les fichiers JSP des results.

Je crée le fichier src/main/webapp/jsp/_include/head.jsp :

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<meta charset="UTF-8">
<title>Ticket</title>
<s:head />

Dans les autres pages JSP, il suffit de remplacer le contenu de la balise <head> par l'inclusion du fichier JSP fraîchement créé.

Par exemple, pour le fichier src/main/webapp/projet/list.jsp :

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>
<html>
<head>
    <%@ include file="../_include/head.jsp"%>
</head>
...

Vous pouvez suivre le même principe pour créer des JSP d'inclusion permettant d'ajouter les scripts javascript, un bandeau de navigation, un footer...

Conclusion

Résumons, ce que nous avons appris dans ce chapitre.

Avec Struts, un cas d'utilisation correspond à une Action. Le traitement à réaliser dans un cas d'utilisation est implémenté dans une classe d'action. Cette classe peut hériter de la classe ActionSupport pour bénéficier de certains mécanismes.

Cette classe d'action et la méthode à appeler par Struts sont configurées dans le mapping Struts (ici, via le fichier struts.xml). Si aucune méthode n'est précisée, la méthode execute de l'action sera appelée.

La méthode de l'action renvoie un String qui est le nom du Result. Les Results sont également configurés dans le mapping Struts. Il est possible de configurer des results globaux.

Enfin, dans les JSP, afin d'accéder aux éléments de Struts, il faut utiliser la taglib fournie par Struts :

<%@ taglib prefix="s" uri="/struts-tags" %>

Ces tags utilisent une syntaxe particulière : OGNL.

Exemple de certificat de réussite
Exemple de certificat de réussite