Table des matières

Dans ce TP vous allez déployer vos premiers servlets et découvrir la structure d’un conteneur de servlet (Tomcat). Pour le moment, pas d’IDE (ex: Eclipse), on code à l’ancienne. Les seules choses dont vous avez besoin pour réaliser les tâches demandées sont un terminal et un navigateur web.

Installation de Tomcat

Votre premier Servlet : Hello World!

Les différentes applications présentes dans le serveur Tomcat se trouvent dans le dossier webapps. C’est ici que nous allons créer notre premier servlet. Dans ce dossier, créez un répertoire hello. Les fichiers de ce répertoire seront accessibles via http://localhost:8080/hello.

Dans le dossier hello, créez un dossier WEB-INF. C’est ici que seront situées les informations liées à la configuration de l’application. Dans le dossier WEB-INF, ajoutez un dossier classes. Comme son nom l’indique, c’est ici que devront être déployées les différentes classes utilisées par l’application. Créez enfin un dossier src dans le dossier webapps/hello, puis ajoutez dans ce dossier un nouveau fichier HelloServlet.java, contenant le code suivant.

package fr.ub.m2.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(urlPatterns="/world")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter(); 
        out.println("<html>");
        out.println("<body>");
        out.println("Hello World!");
        out.println("</body>");
        out.println("</html>");
    }
}

Vous pouvez ensuite compiler la classe HelloServlet avec la commande suivante.

$ javac -classpath ../../../lib/servlet-api.jar HelloServlet.java

Déplacez le fichier .class obtenu dans WEB-INF/classes/fr/ub/m2/servlet/. Notez que la fin de l’arborescence correspond au nom du package de HelloServlet.

NB: mkdir -p permet de créer une arborescence de fichiers en une seule commande.

Redémarrez votre serveur (shutdown.sh puis startup.sh). En tapant l’adresse http://localhost:8080/hello/world, le serveur fera un appel à la méthode doGet, et affichera une page html avec le contenu ajouté par le PrintWriter.

Traitement des paramètres d’une requête GET

Il est possible de passer des paramètres dans une requête HTTP GET. L’url prend alors la forme suivante: http://localhost:8080/hello/world?name=toto

Modifiez la classe HelloServlet afin que l’url ci-dessus affiche une page html contenant le texte: “Hello toto.” Pour récupérer la valeur d’un paramètre, il faut utiliser :

String parameterValue = req.getParameter("parameterName");

Pour éviter de redémarrer le serveur à chaque fois qu’on fait une modification des fichiers .class, une solution est d’éditer le fichier conf/context.xml et d’ajouter un attribut à la balise Context :

<Context reloadable="true">

Redémarrez le serveur. Lorsque vous ferez des modifications des fichiers .class, le message suivant devrait s’afficher dans les logs de Tomcat : INFO: Reloading Context with name [/hello] is completed.

Requêtes POST

Un servlet peut également traiter des requêtes POST. Ce type de requête peut être envoyé via un formulaire html. Ajoutez dans le dossier webapps/hello le fichier index.html suivant:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>POST</title>
</head>

<body>
  <form action="world" method="POST">
    <h1>Please Fill the Registration Form</h1>
    Enter Your Name <input type="text" name="name"/>
    <input type="submit" value="send" />
</form>
</body>

</html>

Si vous ouvrez la page http://localhost:8080/hello, vous verrez apparaître le formulaire. Si vous cliquez sur le bouton send, une exception HTTP sera renvoyée par le serveur, signifiant que la méthode POST n’est pas supportée par le serveur. Il faut donc ajouter le support de la méthode POST à notre servlet. Ajoutez dans le fichier HelloWorld.java la méthode doPost:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    resp.setContentType("text/plain");
    // TODO: Traiter les parametres de la requete

}

Notez ici que le type de réponse est text/plain, ce qui signifie qu’il est possible de mettre du simple texte dans la réponse, au lieu de texte formaté en Html.

Modifiez le code pour afficher “Hello toto!” lorsque “toto” est entré dans le formulaire.

Headers

Un certain nombre d’informations sont disponibles dans les headers des requêtes, comme le nom de l’hôte, le navigateur du client, etc. La fonction suivante permet d’afficher les headers d’une requête dans une table html. Copiez-la dans votre servlet, puis testez-la avec un appel à cette fonction dans la méthode doGet().

private void printHeaders(HttpServletRequest req, PrintWriter out) {
        out.println("<b>Request Method: </b>" + req.getMethod() + "<br/>");
        out.println("<b>Request URI: </b>" + req.getRequestURI() + "<br/>");
        out.println("<b>Request Protocol: </b>" + req.getProtocol() + "<br/><br/>");
        out.println("<table><thead>\n");
        out.println("\t<tr>\n<th>Header Name</th><th>Header Value</th></tr>\n");
        out.println("</thead><tbody>");
        Enumeration headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) {
                String headerName = (String)headerNames.nextElement();
                out.println("\t<tr>\n\t\t<td>" + headerName + "</td>");
                out.println("<td>" + req.getHeader(headerName) + "</td>\n");
                out.println("\t</tr>\n");
        }
        out.println("</tbody></table>");
}

Pensez à ajouter l’import suivant:

import java.util.Enumeration;

Faites la même chose pour la méthode doPost (pensez à modifier le content-type de la réponse en text/html). Vous remarquerez qu’un nouveau header content-length est apparu. Vous pouvez (et devez) donc lire le contenu de la requête et l’afficher grâce à l’instruction suivante.

String content = req.getReader().readLine();

Vous pouvez utiliser le style css suivant pour mettre en forme correctement le tableau.

table {
    border-collapse: collapse;
    width: 100%;
}
th, td {
    padding: 0.25rem;
    text-align: left;
    border: 1px solid #ccc;
}
tbody tr:nth-child(odd) {
    background: #eee;
}        

Sessions

Il est possible de conserver des données le temps d’une session. Une session se termine lorsque le timeout expire ou lorsque le serveur s’arrête. Modifiez votre servlet pour que le nom de la dernière personne soit affiché si aucun nom n’est passé en paramètre.

La session se comporte comme une table de hachage, associant des clés de type String à des éléments de type Object.

Pour enregistrer un élément dans la session, utilisez la méthode :

req.getSession().setAttribute("name", userName);

Et pour récupérer la valeur d’un élément, utilisez par exemple :

String userName = (String) req.getSession().getAttribute("name");

Forward

Différentes parties de l’application peuvent traiter une même requête. Cette opération s’appelle le forwarding, et elle est transparente du point de vue du client. Le forwarding se fait grâce à la méthode suivante:

req.getRequestDispatcher("/servletToto").forward(req, resp);

Créez un servlet qui forward les requêtes GET et/ou POST vers un servlet différent.

Redirection

Il est possible de rediriger le navigateur du client vers une autre URL, qui peut être externe au serveur (e.g. www.google.fr), graçe à la méthode sendRedirect de la classe HttpServletResponse. Créez un formulaire qui redirige le navigateur du client sur Google, en ayant lancé la recherche avec les mots clés contenus dans le champ de saisie.

Identification

Mettre en place l’exemple d’identification par mot de passe vu en cours en utilisant des sessions et des redirections. Ajouter un formulaire de création de compte (login, password) et stocker les informations d’identification dans le contexte de l’application sous la forme d’une simple table.