Exemples de synthèse

Chapitres traités   

Dans cette étude, nous allons faire une pose et en profiter pour vérifier tout simplement les compétences acquises dans cette première partie consacrée à J2EE. Nous allons élaborer des exemples très simples au travers desquels nous allons justifier les choix des solutions retenues. Du coup, afin d'éviter la complexité, les exemples choisis ne comporteront pas de communication avec une base de données quelconque.

 

Choix du chapitre Choix entre servlet et page JSP

Lorsque nous devons contruire des applications Web, et vu qu'en Java nous avons des technologies un peu concurrentes, avec les servlets et les pages JSP, nous nous posons souvent la question de savoir laquelle des deux est la plus adaptée à mon problème, ou bien même, s'il n'est pas souhaitable d'utiliser les deux ? Doit-on également utiliser systématiquement le modèle MVC ?

Pour cette dernière question, il faut que l'application Web soit un peu conséquence pour que l'investissement vaille le coup. Par ailleurs, il est souhaitable d'utiliser des technologies connexes comme JSF ou struts pour que le résultat soit agréable et facile à mettre en oeuvre.

Sinon que choisir entre servlet et JSP ? En fait, tout dépend si le résultat est l'affichage d'une page Web ou alors de proposer un traitement particulier côté serveur. Dans ce dernier cas, la servlet s'impose puisque nous avons besoin d'écrire essentiellement du code Java. Si vous devez produire une page Web, la question ne se pose pas, il faut prendre la page JSP. Par contre, évitez au maximum de placer du code Java sur cette dernière, sinon c'est très difficile à lire et à maintenir. N'hésitez pas à écrire le code dans des JavaBeans. Ainsi, vous obtenez une séparation entre la présentation et le traitement à réaliser. Toujours pour les pages JSP, n'hésitez pas également à fabriquer des balises personnalisées, avec des outils comme NetBeans, c'est très facile et rapide à mettre au point.

 

Choix du chapitre Premier exemple traité uniquement avec une servlet

Le premier exemple que nous allons mettre en oeuvre permet d'effectuer des calcul sur des opérations de base. Quatres opérandes au maximum sont pris en comptes. Le traitement du calcul est réalisé côté serveur Web au moyen d'une servlet. Le client, lui, propose les calculs au travers d'un navigateur. L'avantage de ce système, c'est nous avons besoin d'une technologie très légère côté client. Ci-dessous se trouve la page d'accueil de l'application Web :

Voici le résultat retourné par l'application Web :

Architecture

Cette application Web est composée de deux éléments qui correspondent finalement aux deux pages que nous venons de voir :

  1. Une page Web statique operation.html.
  2. Une servlet ServletOpération qui après avoir récupérer les informations et traiter le calcul désiré, envoie une page Web relative à l'opération demandée.

 

Descripteur de déploiement

Afin que la bonne page d'accueil operation.html soit affichée et que la servlet ServletOpération soit bien celle qui est demandée pour le traitement, vous devez constituer le descipteur de déploiement <web.xml> qui indique, rappelons-le, comment doit se comporter l'application Web suivant les événements proposés :

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
                 xmlns="http://java.sun.com/xml/ns/j2ee" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   
    <servlet>
        <servlet-name>Opération</servlet-name>
        <servlet-class>ServletOpération</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>Opération</servlet-name>
        <url-pattern>/Operation</url-pattern>
    </servlet-mapping>
    
    <welcome-file-list>
        <welcome-file>operation.html</welcome-file>
     </welcome-file-list>
</web-app>

 

Page d'accueil

 

Operation.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Op&eacute;rations de base</title>
  </head>
  <body>
      <h2>Op&eacute;rations de base</h2>
      <form action="Operation">
            <hr />
            <h4>Op&eacute;rande 1 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 2 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 3 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 4 : <input type="text" name="operandes" value="" /></h4>
            <hr />
            <input type="reset" value="Tout effacer" /> 
            <input type="submit" value="+" name="operation" /> 
            <input type="submit" value="-" name="operation" /> 
            <input type="submit" value="*" name="operation" /> 
            <input type="submit" value="/" name="operation" />           
      </form>
  </body>
</html>

 

Servlet

 

ServletOpération.java
import java.io.*;
import java.net.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class ServletOpération extends HttpServlet {
   
   protected void processRequest(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      response.setContentType("text/html;charset=UTF-8");
      PrintWriter out = response.getWriter();
      
     int nombre=0; 
     int[] opérandes = new int[4]; 
     
     char opération = request.getParameter("operation").charAt(0); 
     
     String[] operandes = request.getParameterValues("operandes");
     for(int i=0; i<operandes.length; i++) 
        if (operandes[i].length()!=0) opérandes[nombre++] = Integer.parseInt(operandes[i]);
     
     
     int résultat = opérandes[0];
     
     switch (opération) {
        case '+' : for (int i=1; i<nombre; i++) résultat += opérandes[i]; break;
        case '-' : for (int i=1; i<nombre; i++) résultat -= opérandes[i]; break;
        case '*' : for (int i=1; i<nombre; i++) résultat *= opérandes[i]; break;
        case '/' : for (int i=1; i<nombre; i++) résultat /= opérandes[i]; break;
     }   
      
        out.println("<html>");
        out.println("<head>");
        out.println("<title>R&eacute;sultat du calcul</title>");
        out.println("</head>");
        out.println("<body>");
        out.print("<h2>"+ ( nombre > 0 ? opérandes[0] : 0 ));
        for(int i=1; i<nombre; i++) {
             out.print(" "+opération + " " + opérandes[i]);
        } 
        out.println(" = "+ résultat +" </h2>");
        out.println("</body>");
        out.println("</html>");
        out.close();
   }
   
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      processRequest(request, response);
   }
   
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      processRequest(request, response);
   }
}

 

Le choix de la servlet est-il judicieux ?

 

Ici, nous avons une bonne partie de traitement, donc pas mal de code Java, et finalement le fait de prendre une servlet pour réaliser la page Web dynamique n'est pas un si mauvais choix que ça. Toutefois, c'est toujours ennuyeux d'avoir systématiquement l'écriture out.println() au moment de la fabrication de la page Web.

 

Choix du chapitre Traiter le même exemple, mais cette fois-ci avec une page JSP

Ici, nous réalisons à la fois le même traitement ainsi que la même construction de la page Web dynamique, mais cette fois-ci c'est la page JSP qui s'occupe de tout cela.

Architecture

Cette application Web est également composée de deux éléments :

  1. Une page Web statique operation.html.
  2. La page JSP Operation.jsp qui après avoir récupérer les informations et traiter le calcul désiré, envoie une page Web relative à l'opération demandée.

Descripteur de déploiement

Cette fois-ci, le descripteur de déploiement est beaucoup plus réduit, puisque vous avez juste à indiquer la page d'accueil de l'application Web :

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <welcome-file-list>
        <welcome-file>operation.html</welcome-file>
    </welcome-file-list>
</web-app>

 

Page d'accueil

Seule, l'action de la balise <form> est changée. En effet, il est nécessaire de préciser cette fois-ci que c'est la page JSP operation.jsp qui doit être solliciter en lieu et place de la servlet ServletOperation :

Operation.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
  <head>
    <title>Op&eacute;rations de base</title>
  </head>
  <body>
      <h2>Op&eacute;rations de base</h2>
      <form action="Operation.jsp">
            <hr />
            <h4>Op&eacute;rande 1 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 2 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 3 : <input type="text" name="operandes" value="" /></h4>
            <h4>Op&eacute;rande 4 : <input type="text" name="operandes" value="" /></h4>
            <hr />
            <input type="reset" value="Tout effacer" /> 
            <input type="submit" value="+" name="operation" /> 
            <input type="submit" value="-" name="operation" /> 
            <input type="submit" value="*" name="operation" /> 
            <input type="submit" value="/" name="operation" />           
      </form>
  </body>
</html>

 

Page JSP

La page JSP remplace donc la servlet. Sur la partie haute de la page (lignes 5 à 20), nous retrouvons le même traitement que pour la servlet. Vu que le codage est séparé de la partie présentation, c'est pas mal, par contre en dessous, ce qui correspond normalement à la mise en page Web, nous trouvons un mélange incongru de balises HTML et de codage Java. Dans cette partie basse (lignes 25 à 38), nous avons vraiment du mal à comprendre ce qui est écrit. Il faut vraiment proscrire ce genre d'écriture.

Operation.jsp
 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 2    "http://www.w3.org/TR/html4/loose.dtd">
 3    
 4 <%
 5      int nombre=0;
 6      int[] opérandes = new int[4]; 
 7      
 8      char opération = request.getParameter("operation").charAt(0); 
 9      String[] operandes = request.getParameterValues("operandes");
10      
11      for(int i=0; i<operandes.length; i++) 
12         if (operandes[i].length()!=0) opérandes[nombre++] = Integer.parseInt(operandes[i]);
13      
14      int résultat = opérandes[0];
15      
16      switch (opération) {
17         case '+' : for (int i=1; i<nombre; i++) résultat += opérandes[i]; break;
18         case '-' : for (int i=1; i<nombre; i++) résultat -= opérandes[i]; break;
19         case '*' : for (int i=1; i<nombre; i++) résultat *= opérandes[i]; break;
20         case '/' : for (int i=1; i<nombre; i++) résultat /= opérandes[i]; break;
21      }
22 %>
23    
24    
25 <html>
26     <head>
27         <title>R&eacute;sultat du calcul</title>
28     </head>
29     <body>
30     <h2><%= nombre > 0 ? opérandes[0] : 0  %>
31         <% 
32                for(int i=1; i<nombre; i++) {
33         %>
34         <%= opération %> <%= opérandes[i] %>
35         <% } %>
36         = <%= résultat %></h2>
37     </body>
38 </html>

 

Le choix de la page JSP est-il judicieux ?

 

Franchement, si nous prenons une page JSP pour tout faire, c'est pas terrible. Dans ce cas là, le choix de la servlet paraît même plus adapté. Toutefois, J'ai bien spécifié que lorsque le résultat est une page Web, il est préférable de prendre directement une page JSP, alors comment faire ? Il faut quand même utiliser une page JSP, mais surtout, il est nécessaire de séparer le code de la partie présentation. Pour réaliser cette séparation, il suffit d'utiliser une classe Java qui s'occupe de tout le traitement, avec en plus des propriétés adaptées à la page Web. Lorsque ces critères sont respectés, la classe Java porte le nom de JavaBean.

 

Choix du chapitre Page JSP avec un JavaBean qui réalise le traitement

Nous allons donc reprendre le même sujet en concervant toutefois la page JSP (très remaniée), en prévoyant en plus un composant JavaBean qui s'occupera de tout le calcul nécessaire au traitement.

Architecture

Cette fois-ci l'application Web est composée de trois éléments :

  1. Une page Web statique operation.html.
  2. La page JSP Operation.jsp qui s'occupe de la mise en page. Toute la partie traitement est déléguée au JavaBean bean.Operation.
  3. Le JavaBean bean.Operation en relation avec la page JSP qui s'occupe de tout le traitement afin d'obtenir les calculs désirés.

 

Page JSP

Cette fois-ci, la page JSP est très réduite. Ce qui est normal puisque le résultat demandé est une simple ligne d'affichage. Sur la partie haute de la page, nous avons la création de l'objet calcul qui représente le JavaBean bean.Operation. Dans la ligne suivante, nous demandons à spécifier automatiquement les propriétés relatives aux paramètres données par la requête HTTP. Les paramètres envoyés par la requête HTTP sont respectivement operandes et operation. Nous réclamons ensuite dans l'affichage, la valeur de la propriété resultat du bean calcul. Cette propriété s'occupe de mettre en forme l'affichage du résultat du calcul.

Operation.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
   
<jsp:useBean id="calcul" class="bean.Operation" />
<jsp:setProperty name="calcul" property="*" />
   
<html>
    <head>
        <title>R&eacute;sultat du calcul</title>
    </head>
    <body>
    <h2><jsp:getProperty name="calcul" property="resultat" /></h2>
    </body>
</html>

 

JavaBean

Je rappelle qu'un JavaBean est avant tout une simple classe Java. Pour être considéré comme un JavaBean, il faut toutefois respecter deux critères :

  1. Avoir un constructeur par défaut,
  2. Posséder des propriétés adaptées à l'utilisation de la page JSP afin de permettre le dialogue entre les deux. Le dialogue se fait au moyen de méthodes dont la signature est bien spécifique. D'après la page JSP et les requêtes HTTP proposées, nous devons donc avoir trois propriétés : operandes, operation et resultat. Les deux premières sont prévues pour modifier l'état du bean alors que la dernière sert à récupérer une valeur. Du coup, les trois méthodes qui représentent ces propriétés doivent avoir la signature spécifique suivante : setOperandes(), setOperation() et getResultat(). Remarquez que nous utilisons le préfixe set pour modifier et get pour lire une propriété. Remarquez également que la première lettre de la propriété est passée en lettre capitale dans le nom de la méthode correspondante.

Souvent les propriétés des JavaBeans correspondent à des attributs, mais ce n'est pas indispensable. Remarquez d'ailleurs, dans le code ci-dessous, qu'il existe une différence entre le type de l'attribut operandes et le paramètre operandes passé en argument de la méthode setOperandes().

bean.Operation.java
package bean;

import java.util.*;

public class Operation {
   private int[] operandes;
   private char operation;
   private int resultat;
   
   public Operation() {    
   }
   
   public void setOperandes(String[] operandes) {
      int nombre=0;
      for (int i=0; i<operandes.length; i++) if (operandes[i].length()!=0) nombre++;
      this.operandes = new int[nombre];
      for (int i=0; i<operandes.length; i++)
         if (operandes[i].length()!=0) this.operandes[i] = Integer.parseInt(operandes[i]);      
   }

   public void setOperation(char operation) {
      this.operation = operation;
   }

   private void calcul() {     
     resultat = operandes[0];
     switch (operation) {
        case '+' : for (int i=1; i<operandes.length; i++) resultat += operandes[i]; break;
        case '-' : for (int i=1; i<operandes.length; i++) resultat -= operandes[i];  break;
        case '*' : for (int i=1; i<operandes.length; i++) resultat *= operandes[i]; break;
        case '/' : for (int i=1; i<operandes.length; i++) resultat /= operandes[i]; break;
     }   
   }

   public String getResultat() {
      calcul();
      String chaîne = ""+operandes[0];
      for(int i=1; i<operandes.length; i++)  chaîne = chaîne+" "+operation + " " + operandes[i];
      chaîne = chaîne +" = "+ resultat;      
      return chaîne;
   }
}

Outre les méthodes qui sont en relation avec la page JSP et qui correspondent aux propriétés désirées, nous pouvons trouver d'autres méthodes ainsi, qu'éventuellement d'autres attributs. Ces éléments supplémentaires sont là pour permettre une meilleure gestion du JavaBean et surtout, une plus grande clarté en séparant bien les différentes tâches à réaliser. C'est le cas ici avec la méthode calcul() qui est privée, et qui ne peut donc être utilisée que par une autre méthode, en l'occurence ici, par la méthode qui représente la propriété resultat.

Cette fois-ci, l'architecture de cette application Web est vraiment idéale. Nous avons bien une séparation entre la partie visuelle représentée par la page Web et le traitement proprement dit mis en oeuvre par le JavaBean. Cette séparation de compétence est très utile. Globalement, nous obtenons une plus grande clarté. La maintenance s'en trouvera d'autant plus facilité.

 

Choix du chapitreSynthèse de toutes les technologies utilisables dans une application Web

Nous allons maintenant traité à peu prêt le même type de sujet que précédemment. Toutefois, cette fois-ci il sera composé de pratiquement tout l'ensemble des technologies que nous avons découvertes dans les études précédentes. Voici d'ailleurs ces principaux éléments :

  1. Il est composé d'une seule pas JSP, découpée en plusieurs fragments, qui capitalise l'ensemble des événements proposés par l'utilisateur. Un affichage est adapté à la situation et une nouvelle orientation est proposée suivant le cas de figure.
  2. Tout l'affichage est traité par cette unique page JSP. Nous en profitons pour utiliser des feuilles de style CSS afin de permettre une présentation agréable.
  3. Tout les différents traitements sont réalisés par des JavaBeans.
  4. La gestion de session est prise en compte.
  5. Si une division par zéro survient une page d'erreur adaptée s'affiche en conséquence.
  6. Afin que la page JSP soit la plus lisible possible, nous mettons également en oeuvre une balise personnalisée.
  7. La page JSP est personnalisable au moyen d'un paramètre d'initialisation prévu dans l'application Web.
  8. Une servlet de filtre est mis en oeuvre afin de recenser et de stocker les requêtes proposées par le client dans un fichier journal.
  9. Nous gérons un historique au moyen d'un bouton à deux états avec libellé changeant.
  10. Nous prévoyons également la création et le téléchargement d'un fichier représentant l'historique au travers d'une servlet.

 

Différentes vues de l'application Web

Voici donc comment se présente notre application Web. La page d'accueil est également la page principale de l'application en ce sens que ce sera toujours elle qui sera visible quelque soit les requêtes proposées par l'utilisateur client, et son apparence varie en fonction de ses désirs.

Notamment, il est possible de choisir le nombre d'opérandes afin d'effectuer les calculs requis. Cela se fait au travers d'une liste dont le nombre de valeurs est configurable dans le descripteur de déploiement <web.xml> de l'application Web. Il est également possible de visualiser ou pas l'historique des calculs proposés par l'opérateur. Il est même permis de récupérer cet historique sous forme de fichier texte.

Remarquez au passage le changement du libellé du bouton suivant que l'historique est affiché ou pas.
.

Lorsqu'une erreur de type division par zéro survient, une page d'erreur est alors sollicitée et reprend une grande partie de la présentation de la page principale de l'application Web. Seule la partie propre au résultat est changée afin d'afficher un message d'alerte approprié. Par ailleurs, la partie historique n'est plus proposée.

 

Descripteur de déploiement <web.xml>

Je vais commencer comme d'habitude par le descripteur de déploiement qui décrit le comportement souhaitable de l'application Web. Voici les éléments qui le compose :

  1. <welcome-file-list> : Page d'accueil désignée par la page principale de l'application Web Operation.jsp.
  2. <filter> + <filter-mapping> : Filtre de servlet prévu afin de mettre en oeuvre la journalisation des événements dans un fichier log sur le serveur Web. Attention, les balises du filtre doivent être placés en dernier après les autres servlets éventuelles.
  3. <servlet> + <servlet-mapping> : Servlet qui permet d'envoyer sous forme de texte l'historique de la session.
  4. <context-param> : Creation d'un paramètre NombreMaxi concernant l'application Web entière. Ce paramètre configure le nombre maximum d'opérandes possible pour réaliser les calculs élémentaires.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   
    <context-param>
        <param-name>NombreMaxi</param-name>
        <param-value>7</param-value>
    </context-param>
    <servlet>
        <servlet-name>Fichier</servlet-name>
        <servlet-class>servlet.Fichier</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Fichier</servlet-name>
        <url-pattern>/Historique.txt</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>FiltreJournal</filter-name>
        <filter-class>servlet.Journal</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FiltreJournal</filter-name>
        <url-pattern>/Operation.jsp</url-pattern>
    </filter-mapping>
    
    <welcome-file-list>
        <welcome-file>
            Operation.jsp
        </welcome-file>
    </welcome-file-list>
</web-app>

 

Page principal Operation.jsp qui est toujours la page visible (mise à part si une erreur se produit)

Cette page est très réduite. Elle comporte juste l'ossature du document HTML et fait ensuite appel aux différents fragments de page qui s'occupe chacun d'une tâche particulière. Généralement, les fragments de page sont très souvent utilisés pour proposer un en-tête du site Web ainsi qu'un pied de page commun. Toutefois, rien n'empêche d'utiliser cette technique afin de réduire la complexité en différents sous-ensembles. Ici, les fragments sont :

  1. style.jspf : Feuille de style CSS de la page.
  2. nombre.jspf : S'occupe de la gestion du nombre d'opérandes pour permettre le calcul adapté à la situation.
  3. operandes.jspf : S'occupe de récupérer la saisie des différents opérandes ainsi que le type de calcul élémentaire désiré.
  4. calcul.jspf : S'occupe de l'affichage du résultat.
  5. historique.jspf : S'occupe de la gestion de l'historique de la session.
Operation.jsp
<%@page errorPage="/WEB-INF/erreur.jsp"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<%@include file="/WEB-INF/jspf/style.jspf"%>  
   
<html>
    <head>
        <title>Op&eacute;rations de base</title>
    </head>
    <body>
      <h2>Op&eacute;rations de base</h2>
      <%@include file="/WEB-INF/jspf/nombre.jspf"%>
      <%@include file="/WEB-INF/jspf/operandes.jspf"%>
      <%@include file="/WEB-INF/jspf/calcul.jspf"%>
      <%@include file="/WEB-INF/jspf/historique.jspf"%>
    </body>
</html>

Cette page principale déclare une page d'erreur erreur.jsp qui sera donc active si une erreur se produit sur n'importe lequel des fragments de page. Toutefois, l'erreur recensée est la division par zéro qui ne sera donc éventuellement provoquée que par le fragment de page correspondant, c'est-à-dire : operandes.jspf.

Lorsque différentes erreurs sont suceptibles de se produire, il faut mettre en oeuvre plusieurs pages d'erreur et aiguiller l'application Web au moyen du descripteur de déploiement en rapport avec le type d'erreur rencontré. Ce n'est pas le cas ici, c'est pour cela que cette simple déclaration suffit.

 

Page d'erreur erreur.jsp

Voici donc la page d'erreur. Nous retrouvons la même ossature que la page principale. Seule une partie est enlevée afin de proposer à la place l'affichage de l'erreur "Attention à la division par zéro". Nous voyons là l'intérêt d'avoir découpé la page principale en plusieurs fragments. Ainsi, la page d'erreur est également très réduite puisqu'elle fait appel aux mêmes fragments. Du coup, nous avons l'impression de rester sur la même page.

erreur.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<%@include file="/WEB-INF/jspf/style.jspf"%>  

<html>
    <head>
        <title>Op&eacute;rations de base</title>
    </head>
    <body>
      <h2>Op&eacute;rations de base</h2>
      <%@include file="/WEB-INF/jspf/nombre.jspf"%>
      <%@include file="/WEB-INF/jspf/operandes.jspf"%>
      <h2 class="erreur">Attention &agrave; la division par z&eacute;ro</h2>
    </body>
</html>

 

Feuille de style pour la présentation de la page - fragment de page style.jspf

Nous allons maintenant passer à l'étude des différents fragments de page. Nous commençons dans l'ordre d'apparition par le fragment style.jspf. Cette partie s'occupe essentiellement de l'aspect visuel de l'ensemble des éléments qui constituent la page. Pour cela, nous utilisons la technique des CSS. Grâce à cette technique, vous pouvez changer l'aspect des balises lorsqu'elles sont utilisées dans une page Web, ou même créer de nouveaux éléments qui pourront être utiliser sur n'importe quel type de balise.

Je ne vais pas beaucoup m'étendre sur cette partie puisque je ne vous est proposé aucun cours à ce sujet. Vous pouvez d'ailleurs ne pas le prendre en compte, le fonctionnement demeurera identique. Toutefois, l'aspect est quand même plus agréable.

Le très gros avantage de la technique des CSS, c'est là aussi de faire une séparation entre le contenu d'une page Web et son aspect visuel. CSS ne s'occupe que de cette dernière partie. Du coup, il est même possible de prendre du balisage XML en lieu et place du HTML puisque normalement les balises HTML s'occupent déjà du formattage de l'information. Effectivement, lorsque vous prenez, par exemple, la balise <H2>, vous spécifier d'avoir une tête de chapitre d'une certaine grosseur. Lorsque tous les navigateurs utilisés prendront bien en compte le XML, il sera vraiment préférable d'abandonner le HTML au profit du XML, surtout d'ailleurs pour les pages Web dynamiques.

style.jspf
 <style type="text/css">

      body {
              font-family: Verdana, Arial, Helvetica, sans-serif;
      }

      h2 {
              color: #669900;
              background-color: #66FF66;
              padding: 10px;
              border : groove;
      }

      h3, .operandes {
              color: #0033CC;
              border: thin dashed;
              padding: 5px;
              background-color: #FFFFCC;
              text-indent: 7px;
      }

      h4 {
              margin: 3px;
      }

      .erreur {
              color: #FF0000;
      }

</style>

Ici, nous définissons quelques aspects que prendrons les balises lorsque nous avons besoin :

  1. <body> : Nous choisissons une police par défaut qui sera donc effective pour l'ensemble de la page Web.
  2. <h2> : Nous définissons les couleurs vertes pour le texte et le fond avec une marge intérieure de 10 pixels et avec une bordure incrustée.
  3. <h3> : Nous définissons également les couleurs, avec pour le texte, une couleur bleu et un fond jaune. La bordure est cette fois-ci avec des pointillés. La marge intérieure est également proposée, mais cette fois-ci, elle est de 5 pixels. Par ailleurs, le texte commence à 7 pixels par rapport au bord gauche.
  4. <h4> : Nous réglons ici, uniquement la marge extérieure qui est très inférieure à celle proposée par défaut. La conséquence, c'est que les champs des opérandes seront très proches.

Nous pouvons créer également de nouveau types d'élements qui pourrons être utilisés avec n'importe laquelle des balises HTML. Ces éléments s'appellent alors des classes, et pour qu'ils soient désignés comme tel, le nom doit impérativement avoir comme préfixe un point. C'est le cas ici de .operandes et de .erreur. Lorsque vous aurez besoin d'utiliser ces classes, il faudra alors solliciter l'attribut class de la balise qui la prendra en compte, par exemple :

<h2 class="erreur">Attention &agrave; la division par z&eacute;ro</h2>

La classe .operandes reprend le même comportement visuel que <h3>, et pour .erreur, nous demandons juste que le texte soit écrit en rouge.

 

Gestion du nombre d'opérandes - fragment de page nombre.jspf avec le JavaBean bean.Nombre

Ce fragment permet de gérer le nombre d'opérandes qui seront nécessaires afin d'effectuer le calcul de base adapté au désir de l'utilisateur. N'oubliez pas qu'il faut qu'il y ait au maximum, une séparation entre l'aspect visuel de la page et le traitement sous-jacent. Si un traitement est effectivement demandé, il y aura donc systématiquement un JavaBean correspondant. C'est le cas ici avec le JavaBean bean.Nombre.

nombre.jspf
<jsp:useBean id="nombre" class="bean.Nombre" scope="session" />
<jsp:setProperty name="nombre" property="valeur" param="valeur" />
<jsp:setProperty name="nombre" property="maxi" value="${initParam.NombreMaxi}" />

<h3>
   <form action="Operation.jsp">
      Nombre d'op&eacute;randes : 
      <select name="valeur">
         ${nombre.options}
      </select>
      <input type="submit" value="Valider ce choix" />
   </form>  
</h3>
bean.Nombre.java
package bean;

public class Nombre {
   private int valeur = 2;
   private int maxi = 2;
   public Nombre() {
   }
   public void setValeur(int n) { valeur = n; }
   public int getValeur() { return valeur; }
   public void setMaxi(int n) { maxi = n; }
   public String getOptions() {
      String résultat = "";
      for (int i=2; i<=maxi; i++) 
         résultat = résultat +"<option "+ (i==valeur ? "selected" : "") +">"+i+"</option>\n";
      return résultat;
   }
}

 


Ce fragment de page s'occupe en premier de créer l'objet bean nombre relatif à la classe JavaBean bean.Nombre. Cet objet existe pendant toute la durée de la session. Ainsi, si l'opérateur choisi de travailler avec trois opérandes pour ses calculs, cela lui évite d'en refaire la demande systématiquement.

Ensuite, nous récupérons l'ensemble des paramètres issue de la requête HTTP. Bien entendu, seuls les paramètres correspondant aux propriétés du JavaBean nombre sont pris en compte par ce dernier. Si aucun paramètre le concernant n'est proposé dans la requête, comme c'est par exemple le cas lors de la première connexion, alors le bean nombre ne change pas d'état. Ici, nous avons besoin dans un premier temps de récupérer éventuellement le paramètre de la requête HTTP valeur qui représente la valeur du nombre d'opérandes désiré par l'opérateur. Pour cela, nous demandons un changement de propriété (<jsp:setProperty>) en proposant l'association entre le paramètre de la requête (param) et l'attribut correspondant du bean nombre (property).

Nombre possède en fait deux propriétés, valeur et maxi. La première est éventuellement changée uniquement lorsqu'une requête HTTP est envoyée dans ce sens. Cela se produit lorsque nous appuyons sur le bouton "Valider ce choix". Remarquez au passage que les attributs possèdent des valeurs non-nulles par défaut. Ainsi au démarrage de la session, le nombre d'opérandes est fixé par la valeur 2.

Nous proposons ensuite une deuxième demande de changement de propriété sur ce même bean mais en précisant cette fois-ci le nom de la propriété concernée. Il s'agit de la propriété maxi, et le changement proposé devra donc être impérativement prise en compte par le bean nombre. Ici, en l'occurence, la valeur proposée est issue du paramètre de contexte de l'application Web (initParam) défini par le descripteur de déploiement. Ce paramètre s'appelle NombreMaxi.

La technique qui consiste à utiliser un paramètre de contexte est intéressante. En effet, lorsque vous désirez changer, par exemple, le nombre maximum d'opérandes qui apparaît dans la liste de choix, vous n'avez pas besoin de tout recompiler, il suffit simplement de changer la valeur proposée, directement dans le descripteur de déploiement.

 


Ce fragment intègre ensuite une balise <form> dont l'action est de refaire appel à la page principale Operation.jsp de nouveau. Dans cette balise, nous trouvons également deux autres balises imbriquées : <select> qui propose la liste du nombre d'opérandes et le bouton "Valider ce choix" qui sert de confirmation de la part de l'opérateur. Lorsque l'opérateur clique sur ce bouton, la requête HTTP est effectivement envoyée à la page Operation.jsp avec en plus le paramètre valeur contenant le nombre d'opérandes choisi.

Pour avoir le nombre d'options variables par rapport au nombre maximum d'opérandes possibles, remarquez bien que la page JSP sollicite la propriété options du bean nombre. Cette dernière fabrique alors une chaîne de caractère qui intègre les balises imbriquées <option> de la balise <select> avec en plus la mise en place de l'attribut selected suivant le dernier nombre d'opérande choisi par l'opérateur. Ainsi, nous pouvons effectuer une suite d'opérations de base toujours avec le même nombre d'opérandes.

La mémorisation du nombre d'opérandes impose que ce bean.Nombre soit placée dans la session, ce qui se fait au moyen de l'attribut scope de la balise <jsp:useBean>.

 

Récupération de la valeur des opérandes - fragment de page operandes.jspf

Ce fragment de page nous permet de récupérer les valeurs saisies par l'opérateur afin d'effectuer le calcul de l'opération de base souhaitée. Pour cela, nous avons besoin de choisir le type d'opération ainsi que la valeur de chacun des opérandes. Nous intégrons donc une nouvelle balise <form> dont l'action est une nouvelle fois Operation.jsp. La requête HTTP est envoyée à la page principale par l'intermédiaire d'un des boutons représentant le type d'opération à traiter. Deux paramètres alors sont envoyés avec la requête, le paramètre operandes qui est un tableau de valeur (vue qu'il y a plusieurs opérandes), et le paramètre operation qui indique le caractère correspondant au symbole de l'opération à traiter.

operandes.jspf
<%@taglib uri="/balises" prefix="tag"%>

<div class="operandes" >
 <form action="Operation.jsp">
   <tag:repeter fois="${nombre.valeur}">         
      <h4>Op&eacute;rande ${indice} : 
            <input type="text" name="operandes" value="" width="5" />
      </h4>
   </tag:repeter>
   <h4>.....................
      <input type="submit" value="+" name="operation" />
      <input type="submit" value="-" name="operation" />
      <input type="submit" value="*" name="operation" />
      <input type="submit" value="/" name="operation" />
   </h4>
 </form>
</div>

Le nombre d'opérandes est variable. Du coup, afin d'éviter d'écrire du code Java à l'intérieur de ce fragment de page, notamment avec la mise en place d'une itérative de type for, j'ai plutôt souhaité créer une nouvelle balise personnalisée que je nomme <repeter>. Cette balise comporte un attribut dénommé fois dont la valeur est ici précisé par la propriété valeur du bean nombre. Vous remarquez également la présence de l'attribut indice qui est utile afin de placer une numérotation automatique sur les opérandes. Cet attribut est également prise en charge par notre balise personnalisée <repeter>.

La première ligne de ce fragment de page déclare l'utilisation d'une balise personnalisée dont la bibliothèque de balise se situe à l'uri /balises. Chaque balise de cette bibliothèque (ici, il n'y en a qu'une) devra également utiliser l'espace de nom tag.

A chaque fois que vous créez une balise personnalisée, vous devez le faire en deux étapes par l'intermédiaire de deux structures :

  1. Bibliothèque de balises : D'abord, vous devez spécifier comment doit se comporter la page JSP face à cette nouvelle balise. Est-ce que par exemple cette balise possède un corps et, si c'est le cas, est-ce que ce corps peut être interprété ; est-ce que cette balise comporte également des attributs et comment réagir par rapport à eux, etc.
  2. Gestionnaire de balise : Ensuite, il est nécessaire de réaliser le traitement correspondant à cette balise. Pour cela, vous devez contruire une classe spécifique qui est presque similaire à un JavaBean, mais qui hérite de la classe SimpleTagSupport disposant ainsi d'un certain nombre de méthodes plus adaptées à la situation.

 


La première démarche consiste donc à implémenter une bibliothèque qui sert de descripteur de balises, ici balises.tld. Ce fichier décrit donc l'ensemble des balises personnalisées que comporte votre application Web. Ici, nous avons besoin de décrire une seule balise.

balises.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
   
  <tlib-version>1.0</tlib-version>
  <short-name>balises</short-name>
  <uri>/balises</uri>
  
  <tag>
    <name>repeter</name>
    <tag-class>tag.Repeter</tag-class>
    <body-content>scriptless</body-content>

    <attribute>
      <name>fois</name>
      <required>true</required>
      <type>int</type>
      <rtexprvalue>yes</rtexprvalue>
    </attribute>
    
  </tag>
</taglib>

La première partie de ce descripteur est commune à toutes les balises de la bibliothèque avec, le numéro de version, l'espace de nom utilisée et l'URI de localisation. Cette partie est obligatoire, mais seule la balise <uri> me paraît intéressante. Faites en sorte d'avoir l'URI la plus courte possible comme ici /balises.

Ensuite, chaque balise est délimitée par une balise <tag>. Dans cette balise, vous spécifier le nom qui apparaitra dans la page JSP (<repeter>), la classe correspondante qui sera lancer afin d'interpréter le code interne correspondant ( <tag-class>). Ensuite, nous devons indiquer impérativement si cette balise possède un corps et si ce corps doit être copié tel quel ou au contraire interprété (<body-content>) . En ce qui nous concerne, le corps est interprété puisque nous avons besoin d'utiliser l'attribut de page : indice. Du coup, nous devons spécifier la valeur scriptless.

Ensuite, nous devons décrire la valeur de l'attribut fois correspondant à la balise <repeter>. Ainis, il faut préciser son nom (<name>), si il est obligatoire ou pas (<required>), quel est son type (<type>) et surtout si la valeur proposée à l'attribut est une constante ou une valeur délivrée par une autre entité comme ici par la propriété valeur du bean nombre. Dans ce cas là, il est nécessaire de valider la balise <rtexrvalue>.

 


Dans la deuxième démarche nous allons écrire le codage correspondant au traitement souhaité par l'utilisation de cette balise. Ici, nous désirons que le contenu que nous plaçons à l'intérieur de <repeter> soit écrite x fois avec la possibilité supplémentaire de gérer un indice dont la première valeur débute à 1. Ici, le codage s'exprime à l'intérieur du gestionnaire de balise tag.Rapeter.

tag.Repeter.java
package tag;

import java.io.IOException;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;

public class Repeter extends SimpleTagSupport {
   private int fois;
  
   public void setFois(int value) {
      this.fois = value;
   } 
   
   public void doTag() throws JspException, IOException { 
      JspWriter pageJSP = getJspContext().getOut();
      JspFragment corps = getJspBody();
      for (int i=0; i<fois; i++) {
         this.getJspContext().setAttribute("indice", i+1);
         corps.invoke(pageJSP);
      }
   }  
}

Ainsi, le codage doit être implémenté dans une classe qui, lorsque elle décrit le fonctionnement d'une balise personnalisée, s'appelle un gestionnaire de balise. Cette classe doit respecter les spécifications d'un JavaBean et doit implémenter et doit hériter de la classe SimpleTagSupport.

Nous retrouvons effectivement le même type de démarche que pour les JavaBeans. En effet, les attributs des balises correspondent aux attributs de la classe. Ici, nous devons nous occuper de l'attribut fois. Nous le retrouvons effectivement comme attribut de la classe tag.Repeter. Son type est bien un entier.

Ensuite, lorsque nous devons spécifier ce que doit réaliser cette balise, nous devons redéfinir la méthode doTag(). Très souvent, nous devons écrire dans la page JSP elle-même. Pour cela, il est nécessaire de récupérer son contexte et plus précisément son flux de texte, ce qui se fait au moyen de la suite de méthodes getJspContext().getOut(). Dans notre cas, nous devons également récupérer ce qui est écrit dans le corps de la balise <repeter> afin que cette répétition ait bien lieu. Pour localiser ce corps, nous devons faire appel à la méthode getJspBody() et ensuite, pour copier le contenu du corps de la balise et l'envoyer dans la page JSP, nous devons utiliser la méthode invoke(). Enfin, lorsque vous désirez créer un nouvel attribut à cette page JSP, vous pouvez utiliser tout simplement la méthode setAttribute().

 

Traitement et affichage du calcul demandé - fragment de page calcul.jspf

Passons maintenant au fragment de page relatif au calcul. Dans le chapitre précédent, nous avons déjà mis en oeuvre cette technique. Je le replace donc juste à titre de rappel. Ce fragment crée donc un bean calcul correspondant à la classe bean.Operation. Ensuite, ce fragment spécifie l'ensemble de propriétés de ce bean si les paramètres correspondant sont requis par une quelconque requête HTTP. Ensuite, nous affichons le traitement du calcul au moyen de la propriété resultat du bean calcul.

calcul.jspf
<jsp:useBean id="calcul" class="bean.Operation" />
<jsp:setProperty name="calcul" property="*" />

<h2>Résultat : ${calcul.resultat}</h2>
Operation.java
package bean;

import java.util.*;

public class Operation {
   private int[] operandes = new int[7];
   private int nombre;
   private char operation;
   private int resultat;
   
   public Operation() {    
   }
   
   public void setOperandes(String[] operandes) {
      nombre = 0;
      for (int i=0; i<operandes.length; i++)
         if (operandes[i].length()!=0) this.operandes[nombre++] = Integer.parseInt(operandes[i]);      
   }
   public void setOperation(char operation) {
      this.operation = operation;
   }
   private void calcul() {     
     resultat = operandes[0];
     switch (operation) {
        case '+' : for (int i=1; i<nombre; i++) resultat += operandes[i]; break;
        case '-' : for (int i=1; i<nombre; i++) resultat -= operandes[i]; break;
        case '*' : for (int i=1; i<nombre; i++) resultat *= operandes[i]; break;
        case '/' : for (int i=1; i<nombre; i++) resultat /= operandes[i]; break;
     }   
   }
   public String getResultat() {
      calcul();
      String chaîne = ""+(nombre>0 ? operandes[0] : "Aucun");
      for(int i=1; i<nombre; i++)  chaîne = chaîne+" "+operation + " " + operandes[i];
      if (nombre>0) chaîne = chaîne +" = "+ resultat;      
      return chaîne;
   }
}

 

Gestion d'un historique qui fonctionne pendant toute la durée de la session - fragment de page historique.jspf

Le dernier fragment de page concerne la gestion d'un historique qui capture tous les calculs demandés par un client dans une même session. Nous allons appliquer la même démarche préconisée jusqu'à présent, c'est-à-dire : un JavaBean bean.Historique qui s'occupe du traitement proprement dit de cet historique, et l'écriture de balises ou d'expressions EL dans le fragment de page historique.jspf.

historique.jspf
<jsp:useBean id="historique" class="bean.Historique" scope="session" />
<jsp:setProperty name="historique" property="visible" param="visible" />
<jsp:setProperty name="historique" property="calcul" value="${calcul}" />
    
<h4 class="operandes">   
   <form action="Operation.jsp">
       Historique : <input type="submit" value="${historique.texte}" name="visible" />
       ${historique.liste}  
   </form>
   <form action="Historique.txt">
      <input type="submit" value="Télécharger" />
   </form>
</h4>
bean.Historique.java
package bean;

import java.util.*;

public class Historique {
   private ArrayList<String> liste = new ArrayList<String>();
   private boolean visible = false;
   private String texte = "Visualiser historique";
   
   public Historique() {    
   }
   public void setCalcul(Operation operation) {
      if (!operation.getResultat().equals("Aucun"))
         liste.add(operation.getResultat());
   }
   public String getListe() {
      String résultat = "<br /><br />";
      for (String valeur : liste) résultat = résultat + ".... "+ valeur + "<br />";
      return visible ? résultat : "";
   }
   public void setVisible(String état) {
      visible = état.equals("Visualiser historique");    
      texte = visible ? "Cacher historique" : "Visualiser historique";
   } 
   public String getTexte() {
      return texte;
   }
   public ArrayList<String> getHistorique() { return liste; }
}

Comme souvent, la première chose à faire est de créer le bean qui s'occupe du traitement. Ici, nous créons le bean historique relatif à la classe bean.Historique. qui doit être actif pendant toute la durée de la session. Ensuite, nous positionnons la propriété visible en correspondance avec le paramètre éventuelle de la requête HTTP qui porte le même nom. Cette requête est sollicité par une action sur le bouton à deux états "Cacher l'historique" ou "Visualiser l'historique". Cette propriété est de type booléen et a justement pour but de déterminer le prochain état du bean. Elle permet de changer, entre autre, le libéllé du bouton.

Pour connaître le dernier traitement réalisé, il faut être en relation avec le bean calcul. C'est ce que permet de faire la propriété calcul que vous devez impérativement positionner. En effet, même si maintenant, vous ne désirez pas avoir l'affichage de l'historique, vous devez quand même sauvegarder chacun des calculs effectivement traités. De la sorte, plus tard, vous serez à même de proposer l'ensemble des calculs sauvegardés dans l'historique.

Suivant l'état de la propriété visible du bean historique, vous visualisez ou pas l'historique de la session au moyen de la propriété liste.

Nous remarquons dans ce fragment de page deux balises <form>. La première sert à la visualisation ou pas de l'historique dans la page Web. La deuxième permet de récupérer le fichier texte correspondant. Remarquez au passage que l'action demandée dans cette deuxième balise <form> est carrément le nom du fichier.

 

Envoi d'un fichier relatif à l'historique de la session - servlet Fichier.java

Lorsqu'il s'agit de présenter un document HTML fabriqué par un composant du serveur Web, prenez systématiquement une page JSP pour traiter ce genre de problème, quitte à utiliser en parallèle un JavaBean qui permet de séparer le code du traitement de la partie présentation visuelle.

Par contre, lorsque le résultat d'une requête HTTP n'est pas une page Web, mais un autre type de structure, cette fois-ci la servlet paraît plus adaptée. C'est notre cas ici, puisqu'il s'agit d'envoyer un fichier de type texte au client. Ce fichier est l'ensemble des opérations réalisées et stockées dans l'historique de la session.

servlet.Fichier.java
package servlet;

import java.io.*;
import java.net.*;
import java.util.ArrayList;

import javax.servlet.*;
import javax.servlet.http.*;
import bean.Historique;

public class Fichier extends HttpServlet {
   protected void processRequest(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      response.setContentType("application/octet-stream");
      PrintWriter out = response.getWriter();
      Historique historique = (Historique)request.getSession().getAttribute("historique");
      ArrayList<String> liste = historique.getHistorique();
      for (String calcul : liste) out.println(calcul);
      out.close();
   }
   
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      processRequest(request, response);
   }

   protected void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
      processRequest(request, response);
   }

}
historique.jspf
<jsp:useBean id="historique" class="bean.Historique" scope="session" />
<jsp:setProperty name="historique" property="visible" param="visible" />
<jsp:setProperty name="historique" property="calcul" value="${calcul}" />
    
<h4 class="operandes">   
   <form action="Operation.jsp">
       Historique : <input type="submit" value="${historique.texte}" name="visible" />
       ${historique.liste}  
   </form>
   <form action="Historique.txt">
      <input type="submit" value="Télécharger" />
   </form>
</h4>

Le fichier n'existe pas côté serveur Web. On se propose juste d'envoyer les informations requises sous forme de flux de texte. Pour que cela se passe ainsi, c'est dans l'en-tête de la réponse HTTP qu'il faut bien spécifier quel est le type de résultat attendu. Il existe effectivement une méthode spécifique qui précise le type de contenu de la réponse. L'objet response possède la méthode setContentType() qui écrit directement dans l'en-tête de la réponse le type standard MIME choisi.

Je propose comme type MIME la valeur "application/octet-stream". Par ce choix, j'indique juste qu'il s'agit d'un flux binaire qui ne doit pas spécialement être interprété. Ce choix a pour conséquence, côté client, de lancer la boîte de dialogue de téléchragement qui stipule si nous désirons lire ou enregistrer le fichier correspondant. Par contre, l'action que nous sollicitons dans la balise <form> de la page Web (historique.jspf) qui propose la requête est très importante. En effet, ici l'action proposée est Historiqe.txt, ce qui a pour conséquence de proposer un éditeur de texte comme programme de lecture de fichier au moment où la boîte de dialogue de téléchargement apparaît.

Ce qui est spécial dans cette démarche, c'est que le nom que nous plaçons dans l'attribut action de la balise <form> sert à la fois de nom de fichier et sert également à lancer la servlet. C'est grâce au descripteur de déploiement que nous pouvons résoudre ce genre de problème. Je replace d'ailleurs ci-dessous une partie du descripteur de déploiement qui est concernée par cette configuration.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
...
    <servlet>
        <servlet-name>Fichier</servlet-name>
        <servlet-class>servlet.Fichier</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Fichier</servlet-name>
        <url-pattern>/Historique.txt</url-pattern>
    </servlet-mapping>
...
</web-app>

Ceci dit, le flux, par lequel est envoyé l'historique, reste un flux de type texte. C'est finalement le même que d'habitude. L'information est toujours envoyée en direction du navigateur. C'est ce dernier qui interprète qu'il ne s'agit plus d'afficher une page Web classique et qui propose donc, en alternative, l'ouverture de la boîte de dialogue de téléchargement.

Maintenant, il faut effectivement récupérer l'historique correspondant à la session actuelle. Il suffit pour cela de faire appel à la session actuelle au moyen de la méthode getSession() de l'objet request représentant la requête HTTP. Une fois que nous avons obtenu un objet représentant la session, nous pouvons consulter les différents attributs qui la concerne. Ce que nous avons placé dans la session, sont en fait deux objets, les beans nombre et historique.

Du coup, à l'aide de la méthode getAttribute() de l'objet représentant la session, nous pouvons récupérer le bean historique créée par la page JSP historique.jspf. Il suffit maintenant de faire appel à la propriété historique du bean afin d'avoir la liste des opérations réalisées, de récupérer chacun des éléments de cette liste et de les envoyer dans le flux en direction du navigateur, et le tour est joué.

 

Journalisation des événements qui surviennent dans l'application Web - filtre de servlet Journal.java

Nous allons profiter de ce sujet pour mettre en oeuvre une dernière technique qui peut se révéler bien utile afin de collecter un certain nombre de renseignement concernant les différentes interventions du client. Il s'agit ici de mettre en place un filtre qui, comme son nom l'indique, permet de filtrer les informations qui passent au moyen du propocôle HTTP. Ce filtre est constamment sollicité avant de lancer la page normale Operation.jsp. Je rappelle d'ailleurs ci-dessous une partie du descripteur de déploiement qui s'occupe du filtre.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...> 
...

    <filter>
        <filter-name>FiltreJournal</filter-name>
        <filter-class>servlet.Journal</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FiltreJournal</filter-name>
        <url-pattern>/Operation.jsp</url-pattern>
    </filter-mapping>
...
</web-app>

C'est la classe Journal qui traite du comportement du filtre. Cette classe doit implémenter l'interface Filter et décrire le comportement attendu par la redéfinition de la méthode doFilter(). Je rappelle ci-dessous le comportement général d'un filtre

Attention, lorsque vous implémenter l'interface Filter, vous devez vous préoccuper également de redéfinir les méthodes init() et destroy(). La durée de vie du filtre correspond à la durée de vie de la session. Ainsi, au démarrage de la session, le filtre est initialisé au moyen de la méthode init(), et lorsque que le client se déconnecte définitivement (arrêt du navigateur), la méthode destroy() est également sollicité.

servlet.Journal.java
package servlet;

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Journal implements Filter {
   private ServletContext context;
   
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
      chain.doFilter(request, response);
      HttpServletRequest requête = (HttpServletRequest) request;
      String journal = "\nURL : "+ requête.getRequestURI()
                              + "\nNom du client : "+ requête.getRemoteUser()
                              + "\nPoste hôte Client : "+ requête.getRemoteHost()
                              + "\nAdresse IP du client : "+ requête.getRemoteAddr()
                              + "\nNuméro de port du client : "+ requête.getRemotePort()
                              + "\nParamètres envoyés : "+ requête.getQueryString()
                              + "\n\n";
      context.log(journal);    
   }
  
   public void init(FilterConfig filterConfig) throws ServletException {
      context = filterConfig.getServletContext();
      context.log("Le client se connecte...\n\n");
   }

   public void destroy() {
      context.log("Déconnexion du client...\n\n");
   }
}

La classe Journal possède un attribut représentant l'application Web dans son entier, ce qui s'appelle aussi, contexte de servlet. Avec cet objet, il est ensuite possible d'écrire dans un fichier log. Ce fichier log est créé par le serveur Web. Effectivement, chaque jour, un nouveau fichier log est créé par le serveur Web prévu pour recenser les différents événements survenant sur lui-même. Pour écrire dans le fichier log de la journée en cours, il suffit pour cela d'utiliser tout simplement la méthode log() de l'objet représentant le contexte de l'application Web.

Voici ci-dessous, un exemple de fichier log correspondant au passage de ce filtre :