IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 15 - Informatique distribuée

pages : 1 2 3 4 5 6 7 8 9 10 11 12 

Quand un formulaire est soumis à un servlet, HttpServletRequest est préchargée avec les données du formulaire présentées sous la forme de paires clef/valeur. Si on connaît le nom des champs, il suffit d'y accéder directement avec la méthode getParameter( ) pour connaître leur valeur. Il est également possible d'obtenir un objet Enumeration (l'ancienne forme d'un Iterator) vers les noms des champs, ainsi que le montre l'exemple qui suit. Cet exemple montre aussi comment un seul servlet peut être utilisé pour produire à la fois la page contenant le formulaire et la réponse à cette page (on verra plus tard une meilleure solution utilisant les JSP). Si Enumeration est vide, c'est qu'il n'y a plus de champs ; cela signifie qu'aucun formulaire n'a été soumis. Dans ce cas, le formulaire est élaboré, et le bouton de soumission rappellera la même servlet. Toutefois les champs sont affichés lorsqu'ils existent.

//: c15:servlets:EchoForm.java
// Affiche les couples nom-valeur d'un formulaire HTML
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class EchoForm extends HttpServlet {
  public void service(HttpServletRequest req,
    HttpServletResponse res) throws IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    Enumeration flds = req.getParameterNames();
    if(!flds.hasMoreElements()) {
      // Pas de formulaire soumis -- on en crée un:
      out.print("<html>");
      out.print("<form method=\"POST\"" +
        " action=\"EchoForm\">");
      for(int i = 0; i < 10; i++)
        out.print("<b>Field" + i + "</b> " +
          "<input type=\"text\""+
          " size=\"20\" name=\"Field" + i +
          "\" value=\"Value" + i + "\"><br>");
      out.print("<INPUT TYPE=submit name=submit"+
      " Value=\"Submit\"></form></html>");
    } else {
      out.print("<h1>Your form contained:</h1>");
      while(flds.hasMoreElements()) {
        String field= (String)flds.nextElement();
        String value= req.getParameter(field);
        out.print(field + " = " + value+ "<br>");
      }
    }
    out.close();    
  }
} ///:~

On peut penser en lisant cela que Java ne semble pas conçu pour traiter des chaînes de caractères car le formatage des pages à renvoyer est pénible à cause des retours à la ligne, des séquences escape, et du signe + inévitable dans la construction des objets String. Il n'est pas raisonnable de coder une page HTML quelque peu conséquente en Java. Une des solutions est de préparer la page en tant que fichier texte séparé, puis de l'ouvrir et de la passer au serveur Web. S'il fallait de plus effectuer des substitutions de chaînes dans le contenu de la page, ce n'est guère mieux car le traitement des chaînes en Java est très pauvre. Si vous rencontrez un de ces cas, il serait préférable d'adopter une solution mieux appropriée (mon choix irait vers Python ; voici une version incluse dans un programme Java appelé JPython) qui génère une page-réponse.

Les Servlets et le multithreading

Le conteneur de servlet dispose d'un ensemble de threads qu'il peut lancer pour traiter les demandes des clients. On peut imaginer cela comme si deux clients arrivant au même moment étaient traités simultanément par la méthode service( ). En conséquence la méthode service( ) doit être écrite d'une manière sécurisée dans un contexte de thread. Tous les accès aux ressouces communes (fichiers, bases de données) demandent à être protégés par le mot clef synchronized.

L'exemple très simple qui suit utilise une clause synchronized autour de la méthode sleep( )du thread. En conséquence les autres threads seront bloqués jusqu'à ce que le temps imparti (cinq secondes) soit écoulé. Pour tester cela il faut lancer plusieurs instances d'un navigateur puis lancer ce servlet aussi vite que possible ; remarquez alors que chacun d'eux doit attendre avant de voir le jour.

//: c15:servlets:ThreadServlet.java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ThreadServlet extends HttpServlet {
  int i;
  public void service(HttpServletRequest req,
    HttpServletResponse res) throws IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    synchronized(this) {
      try {
        Thread.currentThread().sleep(5000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
    out.print("<h1>Finished " + i++ + "</h1>");
    out.close();    
  }
} ///:~

On peut aussi synchroniser complètement la servlet en mettant le mot clef synchronized juste avant la méthode service( ). En réalité, l'unique justification pour utiliser la clause synchronized à la place de cela est lorsque la section critique se trouve dans un chemin d'exécution qui ne doit pas être exécuté. Dans un tel cas, il serait préférable d'éviter la contrainte de synchronisation à chaque fois en utilisant une clause synchronized. Sinon, chaque thread particulier devrait systématiquement attendre, il vaut donc mieux synchroniser la méthode en entier.

Gérer des sessions avec les servlets

HTTP est un protocole qui ne possède pas la notion de session, on ne peut donc savoir d'un appel serveur à un autre s'il s'agit du même appelant ou s'il s'agit d'une personne complètement différente. Beaucoup d'efforts ont été faits pour créer des mécanismes permettant aux développeurs Web de suivre les sessions. À titre d'exemple, les compagnies ne pourraient pas faire de e-commerce si elles ne gardaient pas la trace d'un client, ainsi que les renseignements qu'il a saisi sur sa liste de courses.

Il existe plusieur méthodes pour suivre une session, mais la plus commune utilise les » cookies persistants, « qui font intégralement partie du standard Internet. Le HTTP Working Group de l'Internet Engineering Task Force a décrit les cookies du standard officiel dans RFC 2109 (ds.internic.net/rfc/rfc2109.txt ou voir www.cookiecentral.com).

Un cookie n'est pas autre chose qu'une information de petite taille envoyée par un serveur Web à un navigateur. Le navigateur sauvegarde ce cookie sur le disque local, puis lors de chaque appel à l'URL associée au cookie, ce dernier est envoyé de manière transparente en même temps que l'appel, fournissant ainsi au serveur l'information désirée en retour (en lui fournissant généralement d'une certaine manière votre identité). Toutefois les clients peuvent inhiber la capacité de leur navigateur à accepter les cookies. Si votre site doit suivre un client qui a inhibé cette possibilité, alors une autre méthode de suivi de session doit être intégrée à la main (réécriture d'URL ou champs cachés dans un formulaire), car les fonctionnalités de suivi de session intégrées à l'API servlet sont construites autour des cookies.

La classe Cookie

L'API servlet (à partir de la version 2.0) fournit la classe Cookie. Cette classe inclut tous les détails de l'en-tête HTTP et permet de définir différents attributs de cookie. L'utilisation d'un cookie consiste simplement à l'ajouter à l'objet réponse. Le constructeur a deux arguments, le premier est un nom du cookie et le deuxième une valeur. Les cookies sont ajoutés à l'objet réponse avant que l'envoi ne soit effectif.

Cookie oreo = new Cookie("TIJava", "2000");
res.addCookie(cookie);

Les cookies sont récupérés en appelant la méthode getCookies( ) de l'objet HttpServletRequest, qui renvoie un tableau d'objets Cookie.

Cookie[ « cookies = req.getCookies();

En appelant getValue( )pour chaque cookie, on obtient une String initialisée avec le contenu du cookie. Dans l'exemple ci-dessus, getValue("TIJava")renverrait une String contenant » 2000.]

La classe Session

Une session consiste en une ou plusieurs requêtes de pages adressées par un client à un site Web durant une période définie. Par exemple, si vous faites vos courses en ligne, la session sera la période démarrant au moment où vous ajoutez un achat dans votre panier jusqu'au moment où vous envoyez effectivement la demande. Chaque achat ajouté au panier déclenchera une nouvelle connexion HTTP, qui n'a aucun rapport ni avec les connexions précédentes ni avec les achats déjà inclus dans votre panier. Pour compenser ce manque d'information, les mécanismes fournis par la spécification des cookies permet au servlet de suivre la session.

Un objet servlet Session réside du côté serveur sur le canal de communication ; son rôle est de capturer les données utiles à propos du client pendant qu'il navigue sur votre site Web et qu'il interagit avec lui. Ces données peuvent être pertinentes pour la session actuelle, comme les achats dans le panier, ou bien peuvent être des information d'authentification fournies lors de l'accès du client au site Web, et qu'il n'y a pas lieu de donner à nouveau durant un ensemble particulier de transactions.

La classe Session de l'API servlet utilise la classe Cookie pour effectuer ce travail. Toutefois, l'objet Session n'a besoin que d'une sorte d'identifiant unique stocké chez le client et passé au serveur. Les sites Web peuvent aussi utiliser les autres systèmes de suivi de session mais ces mécanismes sont plus difficiles à mettre en oeuvre car ils n'existent pas dans l'API servlet (ce qui signifie qu'on doit les écrire à la main pour traiter le cas où le client n'accepte pas les cookies).

Voici un exemple implémentant le suivi de session au moyen de l'API servlet :

//: c15:servlets:SessionPeek.java
// Utilise la classe HttpSession.
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SessionPeek extends HttpServlet {
  public void service(HttpServletRequest req,
  HttpServletResponse res)
  throws ServletException, IOException {
    // Obtenir l'Objet Session avant tout
    // envoi vers le client.
    HttpSession session = req.getSession();
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    out.println("<HEAD><TITLE> SessionPeek ");
    out.println(" </TITLE></HEAD><BODY>");
    out.println("<h1> SessionPeek </h1>");
    // Un simple compteur pour cette session.
    Integer ival = (Integer)
      session.getAttribute("sesspeek.cntr");
    if(ival==null)
      ival = new Integer(1);
    else
      ival = new Integer(ival.intValue() + 1);
    session.setAttribute("sesspeek.cntr", ival);
    out.println("You have hit this page <b>"
      + ival + "</b> times.<p>");
    out.println("<h2>");
    out.println("Saved Session Data </h2>");
    // Boucler au travers de toutes les données de la session:
    Enumeration sesNames =
      session.getAttributeNames();
    while(sesNames.hasMoreElements()) {
      String name =
        sesNames.nextElement().toString();
      Object value = session.getAttribute(name);
      out.println(name + " = " + value + "<br>");
    }
    out.println("<h3> Session Statistics </h3>");
    out.println("Session ID: "
      + session.getId() + "<br>");
    out.println("New Session: " + session.isNew()
      + "<br>");
    out.println("Creation Time: "
      + session.getCreationTime());
    out.println("<I>(" +
      new Date(session.getCreationTime())
      + ")</I><br>");
    out.println("Last Accessed Time: " +
      session.getLastAccessedTime());
    out.println("<I>(" +
      new Date(session.getLastAccessedTime())
      + ")</I><br>");
    out.println("Session Inactive Interval: "
      + session.getMaxInactiveInterval());
    out.println("Session ID in Request: "
      + req.getRequestedSessionId() + "<br>");
    out.println("Is session id from Cookie: "
      + req.isRequestedSessionIdFromCookie()
      + "<br>");
    out.println("Is session id from URL: "
      + req.isRequestedSessionIdFromURL()
      + "<br>");
    out.println("Is session id valid: "
      + req.isRequestedSessionIdValid()
      + "<br>");
    out.println("</BODY>");
    out.close();
  }
  public String getServletInfo() {
    return "A session tracking servlet";
  }
} ///:~

À l'intérieur de la méthode service( ), la méthode getSession( ) est appelée pour l'objet requête et renvoie un objet Session associé à la requête. L'objet Session ne voyage pas sur le réseau, il réside sur le serveur et est associé à un client et à ses requêtes.

La méthode getSession( ) possède deux versions : sans paramètres, ainsi qu'elle est utilisée ici, et getSession(boolean). L'appel de getSession(true) est équivalent à getSession( ).Le boolean sert à indiquer si on désire créer l'objet session lorsqu'on ne le trouve pas. L'appel le plus probable est getSession(true), d'où la forme getSession( ).

L'objet Session, s'il n'est pas nouveau, nous donne des informations sur le client à partir de visites antérieures. Si l'objet Session est nouveau alors le programme commencera à recueillir des informations à propos des activités du client lors de cette visite. Le recueil de cette information est effectué au moyen des méthodes setAttribute( ) et getAttribute( ) de l'objet session.

java.lang.Object getAttribute(java.lang.String)
void setAttribute(java.lang.String name,
                  java.lang.Object value)

L'objet Session utilise une simple paire nom/valeur pour garder l'information. Le nom est du type String, et la valeur peut être n'importe quel objet dérivé de java.lang.Object. SessionPeek garde la trace du nombre de fois où le client est revenu pendant cette session, au moyen d'un objet Integer nommé sesspeek.cntr. Si le nom n'existe pas on crée un Integer avec une valeur de un, sinon on crée un Integer en incrémentant la valeur du précédent. Le nouvel Integer est rangé dans l'objet Session. Si on utilise la même clef dans un appel à setAttribute( ), le nouvel objet écrase l'ancien. Le compteur incrémenté sert à afficher le nombre de visites du client pendant cette session.

La méthode getAttributeNames( ) est en relation avec getAttribute( ) et setAttribute( ) et renvoie une énumération des noms des objets associés à l'objet Session. Une boucle while de SessionPeek montre cette méthode en action.

Vous vous interrogez sans doute sur la durée de vie d'un objet Session. La réponse dépend du conteneur de servlet qu'on utilise ; généralement la durée de vie par défaut est de 30 minutes (1800 secondes), ce que l'on peut voir au travers de l'appel de getMaxInactiveInterval( ) par ServletPeek. Les tests semblent montrer des résultats différents suivant le conteneur de servlet utilisé. De temps en temps l'objet Session peut faire le tour du cadran, mais je n'ai jamais rencontré de cas où l'objet Session disparaît avant que le temps spécifié par « inactive interval » soit écoulé. On peut tester cela en initialisant « inactive interval » à 5 secondes au moyen de setMaxInactiveInterval( )puis voir si l'objet Session est toujours là ou au contraire a été détruit à l'heure déterminée. Il se pourrait que vous ayez à étudier cet attribut lorsque vous choisirez un conteneur de servlet.

Faire fonctionner les exemples de servlet

Si vous ne travaillez pas encore sur un serveur d'applications gérant les servlets Sun ainsi que les technologies JSP, il vous faudra télécharger l'implémentation Tomcat des servlets Java et des JSP, qui est une implémentation libre et « open-source » des servlets, et de plus l'implémentation officielle de référence de Sun. Elle se trouve à jakarta.apache.org.

Suivez les instructions d'installation de l'implementation Tomcat, puis éditez le fichier server.xml pour décrire l'emplacement de votre répertoire qui contiendra vos servlets. Une fois lancé le programme Tomcat vous pouvez tester vos programmes servlet.

Ceci n'était qu'une brève introduction aux servlets ; il existe des livres entiers traitant de ce sujet. Toutefois, cette introduction devrait vous donner suffisamment d'idées pour démarrer. De plus, beaucoup de thèmes développés dans la section suivante ont une compatibilité ascendante avec les servlets.

Les Pages Java Serveur - Java Server Pages

Les Java Server Pages (JSP) sont une extension standard Java définie au-dessus des extensions servlet. Le propos des JSP est de simplifier la création et la gestion des pages Web dynamiques.

L'implémentation de référence Tomcat, déjà mentionnée et disponible librement sur jakarta.apache.org "font-style: normal">, supporte automatiquement les JSP.

Les JSP permettent de mélanger le code HTML d'une page Web et du code Java dans le même document. Le code Java est entouré de tags spéciaux qui indiquent au conteneur JSP qu'il doit utiliser le code pour générer une servlet complètement ou en partie. L'avantage que procurent les JSP est de maintenir un seul document qui est à la fois la page HTML et le code Java qui la gère. Le revers est que celui qui maintient la page JSP doit être autant qualifié en HTML qu'en Java (toutefois, des environnements GUI de construction de JSP devraient apparaître sur le marché).

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Jean-Pierre Vidal, Alban Peignier ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java