Aller au contenu

Exercice 1 - Développement de l'application - V1.0⚓︎

Ca y est, on peut commencer à développer notre application.

1. Création du projet dans eclipse⚓︎

Il faut commencer par créer un projet Web dynamic, qu'on appelera Boutique. Il faut penser à vérifier le runtime utilisé (tomcat9) et à cocher la case Generate web.xml deployment descriptor :

Nouveau projet Web dynamique

Pas de web.xml ?

Si vous avez oublié de cocher cette case, il faudra soit supprimer ce projet et recommencer à l'étape 1, soit ajouter manuellement le fichier 📄web.xml, en respectant la structure attendue.

2. Implémentation du modèle⚓︎

Dans cette première version, le modèle n'est constitué que d'une seule classe, qui modéliser le panier. Il s'agit d'un bean.

  1. Dans la vue Project Explorer, sur le projet Boutique, faire un clic droit, puis :material-mouse:New > Other. Dans l'arborescence, naviguer ensuite vers :material-file-tree:Java > Class.
  2. Cliquer sur Next >.
  3. Indiquer le package : fr.univtours.polytech.boutique.model.

    Warning

    On ne laisse jamais le package par défaut lorsqu'on développe une application avec JEE.

  4. Indiquer le nom de classe : CartBean.

    Remarque

    Il est pratique d'indiquer ce qu'est la classe dans son nom, ici, un bean.

  5. Cette classe est un bean, c'est-à-dire qu'elle implémente l'interface java.io.Serializable. Il faut donc l'ajouter (avec le bouton Add...).

  6. Dans ce bean, il faut créer tous les attributs :
    ☕ Code Java
    1
    2
    3
    4
    5
    6
    private Integer penNumber;
    private Integer feltNumber;
    private Integer rubberNumber;
    private Double cartPrice;
    private Double shippingCost;
    private Double totalPrice;
    
  7. Il faut ensuite générer les getters/setters en faisant un clic droit avec la souris, puis :material-mouse:Source > Generate Getters and Setters...

    Raccourci

    Il peut être très utile de mémoriser le raccourci qui amène directement à :material-mouse:Source : c'est AltShiftS.

  8. Enfin, pour ne pas avoir de warning, on ajoute le champ static tout au début de la classe :

    ☕ Code Java
    1
    private static final long serialVersionUID = 1L;
    

Astuce : bonnes pratiques

Prenez l'habitude, à chaque modification d'une classe, de faire deux choses :

  1. CtrlShiftO : Permet de faire du ménage dans les imports pour ne conserver que ceux utiles.
    Les import java.io.* par exemple sont remplacés par l'import des classes utilisées uniquement.
  2. CtrlShiftF : Permet de formater le code, de la même façon pour tout le monde.
    C'est vraiment un plus si le code de tout le monde est formaté de la même façon. Cela permet de le comprendre beaucoup plus rapidement !
Ne pas utiliser de tabulations

De manière générale, quel que soit le langage (Java, Python, XML, ...) il faut remplacer les Tab par des espaces. Ce site vous indique comment configurer eclipse pour faire cela.

3. Implémentation de la couche métier⚓︎

Par rapport au paradigme MVC, on peut considérer que cette couche fait partie du modèle. En effet, son rôle est de mettre à jour ce dernier.

La couche métier

Cette couche est la couche centrale de l'application. Lorsqu'on la développe, on se demande quels sont les services métiers qu'elle doit fournir. Ici, il y en a un seul, il s'agit de calculer les différents prix affichés sur le panier, c'est-à-dire d'appliquer les 3 règles de gestions.

La couche métier est toujours accessible via une interface. Cette interface indique la liste des services disponibles. Depuis l'extérieur, c'est-à-dire depuis le contrôleur, on souhaite savoir quels sont les services offerts (l'interface), mais on ne veut pas savoir comment ils sont implémentés (la classe qui implémente cette interface).

Nous allons donc créer une interface, proposant une méthode computePrices, qui va prendre le panier en paramètre, appliquer les 3 règles de gestions, et renvoyer le panier mis à jour. Cette interface va être implémentée par une classe.

  1. Dans la vue Project Explorer, sur le projet Boutique, faire un clic droit, puis :material-mouse:New > Other. Dans l'arborescence, naviguer ensuite vers :material-file-tree:Java > Interface.
  2. Cliquer sur Next >.
  3. Indiquer le package : fr.univtours.polytech.boutique.business.
  4. Indiquer le nom de l'interface : StoreBusiness, puis cliquer sur Finish.
  5. Elle contient une méthode computePrices, qui prend un objet CartBean (le panier) en paramètre, et qui renvoie ce même objet modifié :
    ☕ Code Java
    1
    public CartBean computePrices(CartBean cart);
    
  6. En effectuant la même opération, créer une classe StoreBusinessImpl, dans ce même package fr.univtours.polytech.boutique.business, qui implémente cette interface.
  7. Cette classe va contenir 3 champs statiques, qui correspondent au prix de nos trois produits :
    ☕ Code Java
    1
    2
    3
    private final static Double PEN_PRICE = 1.2D;
    private final static Double FELT_PRICE = 1.8D;
    private final static Double RUBBER_PRICE = 2D;;
    
  8. Voici l'implémentation de computePrices :

    ☕ Code Java - Méthode computePrices de StoreBusinessImpl
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public CartBean computePrices(CartBean cart) {
        // Application de la RG1 :
        Double cartPrice = cart.getPenNumber() * PEN_PRICE + ...;
        // Application de la RG2 :
        Double shippingCost = 5D;
        if (cartPrice > ...) {
            shippingCost = ...;
        }
        // Application de la RG3 :
        Double totalPrice = ...;
    
        // Mise à jour du panier :
        cart.setCartPrice(cartPrice);
        ...
        ...
    
        return cart;
    }
    

    Rappel des 3 règles de gestion

    Pour rappel, les trois RG sont :

    • RG1 : Calcul du prix du panier, sachant que :
      • un styo coûte 1,20€,
      • un feutre coûte 1,80€,
      • une gomme coûte 2€.
    • RG2 : Les frais de livraison sont de 5€ si la commande est de moins de 15€, et sont offerts sinon.
    • RG3 : Le prix total de la commande est la somme du prix du panier et des évenutels frais de livraison.
    Remarque

    Lorsqu'on fait un copier/coller du code, les imports ne sont pas toujours faits. S'il y a des erreurs (CartBean est par exemple inconnu), pensez à faire un CtrlShiftO pour les mettre à jour !

  9. Compléter les ... dans la méthode computePrices pour appliquer correctement les 3 RG.

    Note

    À chaque fois que vous verrez des ... dans le code fourni, il faudra compléter ce code ...

C'est bon, notre modèle est en place.

4. Implémentation du contrôleur⚓︎

Le contrôleur est une servlet. Une servlet est une classe qui hérite de HttpServlet de l'API servlet. Il y a ensuite deux façons de déployer une servlet :

Nouvelle version de JEE

JEE, pour Java Entreprise Edition, a changé de nom après la version 8. À partir de la version 9, JEE signifie Jakarta Entreprise Edition.

Il n'y a pas que le nom qui a changé, tous les packages qui commencait par javax commencent maintenant par jakarta. Par exemple, javax.servlet.http.HttpSession a été renommé en jakarta.servlet.http.HttpSession.

C'est pour cela que nous travaillons avec tomcat9 (qui implémente JEE 8), et pas tomcat10 (qui implémente JEE 9) ... 😫 Vous suivez toujours ?

Bref, utiliser JEE 9 présente quelques risques, puisque c'est récent, et il y a souvent des configurations à aller changer manuellement (aller trouver des javax cachés pour les modifier en jakarta) ...

  • Via le descrtipteur de déploiement, c'est-à-dire le fichier 📄web.xml (c'est ce que nous avons fait jusqu'à présent).
  • Via des annotations : c'est ce que nous allons faire maintenant !
Remarque importante !

Dans tous les cas, on choisit une seule de ces deux méthodes, jamais les deux.

  1. On commence par créer la servlet : dans la vue Project Explorer, sur le projet Boutique, faire un clic droit, puis :material-mouse:New > Other. Dans l'arborescence, naviguer ensuite vers :material-file-tree:Web > Servlet.
  2. Cliquer sur Next.
  3. Indiquer le package : fr.univtours.polytech.boutique.controller.
  4. Indiquer le nom de classe : StoreServlet.

    On remarque au passage que l'héritage de javax.servlet.HttpServlet est déjà indiqué.

  5. Cliquer sur Next.

  6. Modifier le champ Name en indiquant controller, puis cliquer sur Next.
  7. Décocher tout sauf Inherited abstract methods et doGet, puis cliquer sur Finish.

    Note

    La servlet n'implémentant que cette méthode, cela signifie qu'elle ne va pouvoir intercepter que les requêtes HTTP dont la méthode est GET. Comme nous le verrons juste après, si nous envoyons une requête HTTP POST, nous obtiendrons une erreur.

    Rappel sur les méthodes HTTP
    • La méthode GET est la méthode par défaut lorsqu'on exécute une requête HTTP depuis son navigateur, c'est-à-dire lorsqu'on saisit l'URL dans la barre du navigateur et qu'on appuie sur Entrée.
      On peut envoyer des paramètres au serveur qui traite cette requete en ajoutant les couples (clef, valeur) dans l'URL, après un ?, séparés par des &.
      Par exemple : http://url_de_mon_appli?param1=valeur1&param2=valeur2&...
    • La méthode POST est appelée lorsqu'on soumet un formulaire, afin d'envoyer des valeurs au serveur, c'est-à-dire lorsqu'on clique sur un bouton. Les paramètres sont alors placés dans le corps de la requête.

    Seule la requête pour afficher la page d'accueil (c'est-à-dire à la première connexion) sera un GET. Lorsque l'utilisateur cliquera sur le bouton Valider après avoir sélectionné ses produits, une méthode POST sera exécutée. Nous verrons un peu plus tard comment faire ce choix de méthode.
    C'est cela qui nous permet de différencier le premier affichage des autres, sans avoir à définir plusieurs servlets.

    On en est là :

    Si tout va bien, ta Servlet doit ressemble à ça :

    ☕ Code Java
    @WebServlet(name = "controller", urlPatterns = { "/controller" })
    public class StoreServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
    
        /**
        * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
        *      response)
        */
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
        }
    
    }
    

    Avec cette méthodes des annotations, il n'y a plus besoin de modifier le fichier 📄web.xml !

    La propriété urlPatterns (on peut lui donner plusieurs valeurs) de l'annotation @WebServlet nous indique comment accéder à cette servlet. Elle a exactement le même rôle que la balise <url-pattern> qu'on utilisait dans le fichier 📄web.xml.

  8. Modifier la méthode doGet pour qu'elle redirige vers la vue store.jsp.

Pourquoi avoir un web.xml?

Maintenant que nous n'avons plus besoin de déclarer les Servlets dans le 📄web.xml, on peut se demander si ce fichier est toujours utile ...

Il va nous permettre d'ajouter d'autres informations, par exemple, de définir une page par défaut !

Ici par exemple, on souhaiterait pouvoir ne saisir que http://localhost:8080/Boutique comme URL, plutôt que http://localhost:8080/Boutique/controller.

Pour cela, dans le fichier 📄web.xml, il suffit de positionner le code suivant à l'intérieur de la balise <web-app> :

📄web.xml
<welcome-file-list>
    <welcome-file>controller</welcome-file>
</welcome-file-list>

Après un redémarrage du serveur, l'application est maintenant disponible ici : http://localhost:8080/Boutique

5. Implémentation de la vue⚓︎

Pour l'instant, il n'y a qu'une seule vue. C'est 📄store.jsp. Cette JSP ne contient pour l'instant que du code HTML :

Où placer les JSP ?

Les JSP (ainsi que les pages HTML, le javascript, le CSS, ...) doivent être placées dans le dossier contenu web. Ce dossier est défini lorsqu'on créée le projet web dynamique. Par défaut, c'est le dossier 📂src/main/webapp :

Contente directory

Java Server Page
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Boutique en ligne</title>
</head>
<body>
    <h1>Sélection des produits</h1>
    <form action="controller" method="post"><!--(1)!-->
        <fieldset>
            <legend>Liste des produits</legend>
            <table>
                <tr>
                    <td>Stylo</td>
                    <td><input type="number" name="penNb"/>
                    <td></td>
                </tr>
                <tr>
                    <td>Feutre</td>
                    <td><input type="number" name="feltNb"/>
                    <td></td>
                </tr>
                <tr>
                    <td>Gomme</td>
                    <td><input type="number" name="rubberNb"/>
                    <td></td>
                </tr>
            </table>
        </fieldset>
        <input type="submit" value="Valider"/>
    </form>

    <fieldset>
        <legend>Récapitulatif du panier</legend>
        <table>
            <tr>
                <td>Panier :</td>
                <td></td>
            </tr>
            <tr>
                <td>Frais livraison :</td>
                <td></td>
            </tr>
            <tr>
                <td>Prix total :</td>
                <td></td>
            </tr>
        </table>
    </fieldset>
</body>
</html>
  1. Le formulaire HTML (balise <form>) permet à l'utilisateur de saisir des informations, et de les envoyer au serveur en cliquant sur le bouton valider (on dit qu'on "soumet" le formulaire.)

    Toutes les balises <input type="*"> permettent d'envoyer une information au serveur, que nous allons pouvoir récupérer grace à la valeur de l'attribut name.

    La propriété action permet d'indiquer quelle URL va être exécutée à la soumission du formulaire (on fait le lien avec le <url-pattern> dans le 📄web.xml).

    Enfin, la propriété method indique avec quelle méthode la requête HTTP est envoyée. Ici, c'est POST, ce qui nous permettra de différencier le premier affichage de la page (méthode GET), et la soumission du formulaire.