Java EE - EPSI POE mars 2017 - David Gayerie Licence Creative Commons

Les servlets

  1. Structure d'une servlet HTTP
  2. Configuration du déploiement d'une servlet
  3. Motif d'URL d'une Servlet
  4. Exercice : traitement d'un formulaire
  5. Utilisation du fichier de déploiement web.xml
  6. Exercice : déploiement à partir du web.xml

Une servlet est un composant Web de Java EE. Elle permet de traiter une requête entrante sur un serveur et de générer une réponse dynamique. La plupart du temps, les servlets sont utilisées pour traiter des requêtes HTTP et générer dynamiquement une réponse.

L'API servlet est définie par la spécification JSR 340 et la version actuelle est la 3.1.

Structure d'une servlet HTTP

Une servlet HTTP est une classe Java qui hérite de la classe javax.servlet.http.HttpServlet :


package fr.epsi;

import javax.servlet.http.HttpServlet;

public class MyServlet extends HttpServlet {

}

Par défaut, la classe javax.servlet.http.HttpServlet fournit des méthodes doXXX (XXX représentant une méthode HTTP) qui seront appelées lorsque la servlet devra traiter une requête HTTP de la méthode correspondante.

HttpServlet dispose donc des méthodes doGet, doPost, doPut... L'implémentation par défaut de ces méthodes consiste à retourner un message d'erreur HTTP. Chaque servlet doit donc redéfinir les méthodes qui la concernent.


package fr.epsi;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * Exemple d'une servlet acceptant les requêtes HTTP GET 
 */
public class MyServlet extends HttpServlet {
	
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, IOException {
    // traitement de la requête et génération du résultat à retourner au client
  }

}

Les méthodes doXXX ont toutes deux paramètres : javax.servlet.http.HttpServletRequest et javax.servlet.http.HttpServletResponse qui représentent respectivement la requête HTTP entrante et la réponse renvoyée par le serveur.

Pour l'instant, les méthodes qui vont nous intéresser sur ces classes sont :

String HttpServletRequest.setCharacterEncoding(String)
Spécifie le format d'encodage des paramètres de la requête. Par défaut, l'encodage utilisé est ISO 8859-1 (Latin-1).
String HttpServletRequest.getParameter(String)
Retourne la valeur d'un paramètre d'une requête GET ou POST. La méthode attend le nom du paramètre et retourne sa valeur ou null si le paramètre n'existe pas.
java.util.Map<java.lang.String,java.lang.String[]> HttpServletRequest.getParameterMap()
Retourne une Map des paramètres d'une requête GET ou POST. La clé dans la Map correspond au nom du paramètre. La valeur est un tableau de chaînes de caractères. En effet, un paramètre peut être présent plusieurs fois dans une requête.
void HttpServletResponse.setContentType(String)
Positionne le type de contenu MIME de la réponse HTTP pour informer le client du format de la réponse. Par exemple : "text/html" pour une page HTML.
void HttpServletResponse.setCharacterEncoding(String)
Indique l'encodage caractère du flux de réponse. L'appel à HttpServletResponse.getWriter() tient compte de l'encodage positionné. Il faut donc appeler cette méthode avant HttpServletResponse.getWriter()
java.io.PrintWriter HttpServletResponse.getWriter()
Retourne un objet de type PrintWriter qui permet d'écrire la réponse dans le flux de sortie. L'objet PrintWriter offre des méthodes write pour générer une réponse au format texte (comme une page HTML).
javax.servlet.ServletOutputStream HttpServletResponse.getOutputStream()
Retourne un objet représentant le flux de sortie en mode binaire. Cette méthode est utile lorsque la réponse générée est au format binaire (comme une image par exemple).

package fr.epsi;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * Une servlet qui salue la personne qui envoie
 * son nom dans le paramètre name.
 */
public class HelloServlet extends HttpServlet {
	
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, IOException {
    req.setCharacterEncoding("utf-8");
    String name = req.getParameter("name");

    resp.setContentType("text/plain");
    resp.setCharacterEncoding("utf-8");
    resp.getWriter().write("Hello " + name + "!");
  }

}

Configuration du déploiement d'une servlet

Une servlet n'est pas une classe Java comme les autres, il s'agit d'un composant Java EE qui va être pris en charge par le serveur d'application. Le serveur d'application a besoin de savoir pour quelle(s) URL cette servlet sera responsable de traiter les requêtes et de fournir la réponse.

La méthode la plus simple pour configurer le déploiement d'une servlet consiste à utiliser l'annotation @WebServlet sur la classe.


package fr.epsi;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, IOException {
    req.setCharacterEncoding("utf-8");
    String name = req.getParameter("name");

    resp.setContentType("text/plain");
    resp.setCharacterEncoding("utf-8");
    resp.getWriter().write("Hello " + name + "!");
  }

}

Pour la servlet ci-dessus, l'annotation @WebServlet précise le motif de l'URL (URL pattern) pour lequel la servlet devra être sollicitée (dans cet exemple "/hello"). Une fois l'application déployée dans un serveur de test en local, une requête de la forme

http://localhost:8080/[nom de l'application]/hello?name=EPSI

devrait répondre

Hello EPSI!

Motif d'URL d'une Servlet

Comme nous l'avons vu dans la section précédente, une servlet pour être déployée a besoin d'un ou plusieurs motifs d'URL indiquant le chemin des requêtes qu'elle prend en charge. Il existe plusieurs syntaxes qui sont toutes équivalentes :

@WebServlet("/hello")
@WebServlet({"/hello"})
@WebServlet(urlPatterns={"/hello"})

Il est possible de donner plusieurs motifs d'URL indiquant que la même servlet peut être sollicitée à partir de chemins différents.

@WebServlet({"/hello", "/bonjour"})
@WebServlet(urlPatterns={"/hello", "/bonjour"})

Enfin, il est possible d'utiliser le caractère générique *. Par contre son utilisation est limitée car il ne peut apparaître que comme premier ou dernier élément d'un motif :

// Toutes les URL se terminant par .html
@WebServlet("*.html")
// Toutes les URL commençant par /hello/
@WebServlet("/hello/*")

Exercice : traitement d'un formulaire

Objectif
Réaliser une application Web qui fournit une page d'accueil présentant un formulaire Web.

Le formulaire permet de saisir son nom et son prénom et de soumettre une requête POST au serveur. Le serveur doit répondre par une page HTML retournant le nom et le prénom saisis ainsi que la date de reception sur le serveur.

Modèle Maven du projet à télécharger
webapp-template.zip
Mise en place du projet
Éditer le fichier pom.xml du template et modifier la balise artifactId pour spécifier le nom de votre projet.
Intégration du projet dans Eclipse
L'intégration du projet dans Eclipse suit la même procédure que celle vue lors de l'introduction à Maven

Utilisation du fichier de déploiement web.xml

Nous avons vu que l'annotation @WebServlet permet d'indiquer comment une servlet doit être déployée dans le serveur. S'il préfère, le développeur a la possibilité de spécifier ces informations dans le fichier de déploiement web.xml plutôt que d'utiliser une annotation.

Pour déclarer une servlet dans une fichier web.xml, il suffit d'associer un identifiant avec le nom de la classe de la servlet. Ensuite, on précise un ou des motifs d'URL pour cette servlet de la façon suivante :

<web-app 
  xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">
  
  <!-- la déclaration de la servlet -->
  <servlet>
    <servlet-name>nomLogiqueDeLaServlet</servlet-name>
    <!-- le nom de la classe implémentant la servlet (précédé du nom du package) -->
    <servlet-class>le.nom.complet.de.la.classe.de.la.Servlet</servlet-class>
  </servlet>
  
  <!-- l'association de la servlet avec un motif d'URL -->
  <servlet-mapping>
    <servlet-name>nomLogiqueDeLaServlet</servlet-name>
    <!-- le motif d'url (par exemple *.html ou /servlet) -->
    <url-pattern>/ma-servlet</url-pattern>
  </servlet-mapping>

</web-app>

Pour rappel, le fichier web.xml doit obligatoirement se trouver dans le répertoire WEB-INF de l'application Web finale. Dans un projet Maven, on placera donc ce fichier dans le répertoire src/main/webapp/WEB-INF.

Exercice : déploiement à partir du web.xml

Objectif
Déployer une servlet en utilisant le fichier de déploiement web.xml. Pour cela, vous allez déployer une servlet fournie par une bibliothèque tierce : barcode4j. Cette servlet génère des codes barres à la volée.
Modèle de projet
Utilisez le même projet que pour l'exercice précédent. Vous verrez ainsi que l'on peut mélanger dans une même application des servlets déclarées par annotations avec des servlets déclarées dans le fichier de déploiement.
Ajout de barcode4j dans le projet
Maven va vous permettre de récupérer automatiquement les bibliothèques qui vont vous être utiles pour cet exercice. Ouvrez le fichier pom.xml du projet et dans la balise <dependencies>, ajoutez les deux dépendences suivantes :
<dependency>
  <groupId>net.sf.barcode4j</groupId>
  <artifactId>barcode4j</artifactId>
  <version>2.1</version>
</dependency>
<dependency>
  <groupId>avalon-framework</groupId>
  <artifactId>avalon-framework-api</artifactId>
  <version>4.2.0</version>
</dependency>

La sauvegarde du fichier pom.xml dans votre IDE doit entraîner un téléchargement par Maven de ces dépendances et leur ajout dans la configuration du projet.

La servlet fournie par barcode4j s'appelle : org.krysalis.barcode4j.servlet.BarcodeServlet

Une fois la servlet déclarée, vous pouvez y accéder en passant des paramètres HTTP détaillés dans la documentation.

Évolution de l'application de traitement d'un formulaire
Faites évoluer l'application de l'exercice précédent. Lorsque l'utilisateur soumet le formulaire, le serveur ajoute dans la page Web de réponse un code barre 2D correspondant au nom envoyé dans le formulaire. Un code barre 2D correspond au type datamatrix pour la servlet barcode4J. Autrement dit, il faut passer type=datamatrix comme paramètre à cette servlet.
Exemple de code barre 2D (datamatrix) généré par barcode4
exemple de code barre de type datamatrix