Servlets
Objectifs Pré-requis : Objectifs : HTML Java Principes de la programmation de pages web dynamiques (PHP, etc.) Configuration Apache TomCat Objectifs : Se familiariser avec les fonctionnalités des servlets
Qu'est-ce que les servlets ? Une interface de l'API Java Un programme Java classique permettant de générer des pages Web dynamiques En général, une servlet étend une des deux classes suivantes, qui implémentent javax.servlet.Servlet : javax.servlet.http.HttpServlet (pour les requêtes HTTP) javax.servlet.GenericServlet (pour les autres requêtes) Les points d'entrée du client vers la servlet sont généralement les méthodes doGet() ou doPost() (selon la méthode HTTP utilisée pour la requête)
Exemple : classe Java java import java.io.*; import javax.servlet.*; import javax.servlet.http.*; package exemple; public class MaPremiereServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Etape 1. Spécifier le type MIME du contenu de la réponse response.setContentType("text/html"); // Etape 2. Récupère le PrintWriter pour envoyer des données au client PrintWriter out = response.getWriter(); // Etape 3. Envoyer l’information au client out.println("<html>"); out.println(" <head><title>Bonjour</title></head>"); out.println(" <body>"); out.println(" <h1> Bonjour à tous </h1>"); out.println(" Il est : " + new java.util.Date()); out.println(" </body></html>"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); java
Exemple : fichier de configuration <?xml version="1.0" encoding="UTF-8"> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"> <servlet> <servlet-name>MaPremiereServlet</servlet-name> <servlet-class>exemple.MaPremierServlet</servlet-class> </servlet> <servlet-mapping> <url-pattern>/exemple</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> XML
Exemple servlet 3.0 Permet d’éviter le fichier de configuration java import java.io.*; import javax.servlet.*; import javax.servlet.http.*; package exemple; @WebServlet(name="MaPremiereServlet", urlPatterns={"/exemple"}) public class MaPremiereServlet extends HttpServlet { … } java
Appel d'une servlet Le serveur ne crée par défaut qu'une seule instance de chaque servlet Un appel d'un client déclenche la création d'un nouveau thread qui manipule cette instance Des appels simultanés génèrent donc plusieurs threads qui manipule la même instance ! Attention donc à la synchronisation
Appel d'une servlet import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class BonThread extends HttpServlet { private int compteur = 0; public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { synchronized(this) { int tmpCompteur = ++compteur; } PrintWriter out = res.getWriter(); out.println("Cette Servlet a été accédée " + tmpCompteur + " fois.");
Appel d'une servlet Pour qu'une instance différente soit générée à chaque appel, on implémente l'interface SingleThreadModel Cela rend parfois la manipulation plus confortable mais consomme plus de mémoire et supprime quelques fonctionnalités
Initialisation d'une servlet On peut personnaliser l'initialisation en redéfinissant la méthode : public void init(ServletConfig config); On peut refuser l'initialisation en lançant dans cette méthode l'exception UnavailableException. Par exemple : throw new UnavailableException("Charge du serveur trop élevée", 120); Refuse l'initialisation et impose un délai de 120 secondes avant de recommencer.
Exécution d'une servlet Redéfinition d'une ou plusieurs de ces méthodes : qui sont appelées en passant deux objets de type HttpServletRequest (requête) et HttpServletResponse (réponse) Méthode Appelée lors de doGet() Requête du client en mode GET doPost() Requête du client en mode POST doHead() Demande d'information sur les entêtes générées par le serveur doOptions() Demande des services disponibles sur le serveur doPut() Dépôt d'un fichier sur le serveur doDelete() Suppression d'un fichier sur le serveur doTrace() Traçage de la requête
Destruction d'une servlet Décidée par le moteur Appel de la méthode public void destroy() que l'on peut redéfinir
Contexte
Contexte d'un servlet On peut définir un contexte permettant d'accéder aux autres ressources utilisées par l'application Web : Servlets JSP Classes utilitaires Documents statiques (HTML ou autres) Applications client méta-informations On peut par exemple accéder au contexte de la façon suivante : public void init(ServletConfig config) { ServletContext contexte = config.getServletContext(); /* suite du code */ }
Contextes sous Tomcat WEB-INF/web.xml : contient la description du contexte WEB-INF/classes/ : contient les classes des servlets WEB-INF/lib/ : contient les .jar utilisées par l'application (et on peut archiver le tout dans un fichier .war) Voir le fichier web.xml généré par eclipse Le contexte est disponible dans toute l’application
Contexte d'un servlet Méthode Action getAttribute(String name) Retourne un attribut du contexte setAttribute(String name, Object value) Ajoute un attribut au contexte getInitParameter(String name) Retourne un paramètre d’initialisation log(String message) Ajoute un message dans le log de la servlet Définition d’un paramètre d’initialisation dans web.xml <context-param> <param-name>contextParam1</param-name> <param-value>valeur 1</param-value> <description>La première valeur du contexte (paramètre d’initialisation)</description> </context-param>
Sessions
À quoi sert une session ? (rappel) Les sessions : Maintiennent l’état du client pendant une série de requêtes du même utilisateur Permettent d’identifier le client Permettent de conserver les données au cours d’une série d’échanges Plusieurs façons de faire en général : Ajouter des variables d’identification à l’URL Utiliser les champs cachés des formulaires Manipuler des cookies Mécanisme transparent en servlet : classe HttpServlet
Créer ou récupérer une session public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Récupère la session HttpSession session = request.getSession(true); // … } Si « true », crée la session si elle n’existe pas Si « false », renvoie null si la session n’existe pas
Stocker des attributs public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Récupère la session HttpSession session = request.getSession(true); // L’âge de l’utilisateur est dans la requête // (issue d’un formulaire) int age = Integer.parseInt(request.getParameter("age_user")); // Conserve l’information dans la session session.setAttribute("age",new Integer(age)); }
Récupérer des attributs public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Récupère la session HttpSession session = request.getSession(true); // Récupère l‘âge de l'utilisateur int age = (int)session.getAttribute("age"); }
Fermer une session public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Récupère la session HttpSession session = request.getSession(true); // Ferme la session session.invalidate(); }
Méthodes de HttpSession Action long getCreationTime() Retourne l'heure de la création de la session Object getAttributes(String Name) Retourne l'objet stocké dans la session sous le nom Name, null s'il n'existe pas String getId() Génère un identifiant de session long getLastAccessedTime() Retourne la date de la requête précédente pour cette session String[] getValueNames() Retourne un tableau contenant le nom de toutes les clés de la session Object getValue(String Name) void invalidate() Supprime la session boolean isnew() Retourne true si la session vient d'être créée, sinon false
Méthodes de HttpSession Action void putValue(String Name, Object Value) Stocke l'objet Value dans la session sous le nom Name void removeValue(String Name) Supprime l'élément Name de la session void setAttribute(String Name, Object Value) int setMaxInactiveInterval(int interval) Définit l'intervalle de temps maximum entre deux requête avant que la session n'expire int getMaxInactiveInterval(int interval) Retourne l'intervalle de temps maximum entre deux requête avant que la session n'expire
Création de cookies public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Création du cookie Cookie c = new Cookie("id","674684641"); // définition de la limite de validité c.setMaxAge(24*3600); // envoi du cookie dans la réponse HTTP response.addCookie(c); }
Récupération des cookies public class Caddie extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Récupération des cookies de la session Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { String name = cookie.getName(); Object value = cookies.getValue(); }
HttpSessionListener Pour surveiller la création et la disparition de sessions Méthodes : void sessionCreated(HttpSessionEvent se); void sessionDestroyed(HttpSessionEvent se); (puis se.getSession() pour obtenir la session en question) À déclarer dans web.xml (ou avec l’annotation @WebListener dans les servlets 3.0) web.xml <listener> <listener-class>exemple.sessionListenerExemple</param-name> </listener>
HttpSessionAttributeListener Pour surveiller l’évolution des attributs de sessions Méthodes : void attributeAdded(HttpSessionBindingEvent event); void attributeRemoved(HttpSessionBindingEvent event); void attributeReplaced(HttpSessionBindingEvent event); (puis event.getSession(), event.getName() et event.getValue() pour obtenir les informations sur l’attribut en question)
Les servlets HTTP
Informations sur le serveur import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class ServeurInfo extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); // on produit du texte ASCII PrintWriter out = res.getWriter(); out.println("Nom du serveur : " + req.getServerName() + " ."); out.println("Logiciel utilisé : " + req.getServletContext().getServerInfo() + " ."); out.println("Port du serveur : " + req.getServerPort() + " ."); out.println("Chemin vers le fichier " + req.getPathInfo() + " : " + req.getPathTranslated(req.getPathInfo()) + " ."); }
Informations sur le client import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class ServeurInfo extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); // on produit du texte ASCII PrintWriter out = res.getWriter(); out.println("Adresse IP du client : " + req.getServerName()); out.println("Nom d'hôte du client : " + req.getRemoteHost()); }
Informations sur la requête import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; public class ServeurInfo extends HttpServlet { // En mode GET (même chose pour POST) public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); // on produit du texte ASCII PrintWriter out = res.getWriter(); Enumeration parametres = req.getParameterNames(); out.println("Affichage des informations sur les paramètres de la requête"); while (parametres.hasMoreElements()) { String nomParametre = (String) parametres.nextElement(); out.println("Le paramètre " + nomParametre + " a la valeur : " + getParameter(nomParametre) + " ."); }
Informations sur les entêtes de la requête le logiciel utilisé par le client. les types MIME des formats de données acceptés. le type d'authentification utilisé dans le cas d'une connexion sécurisée. la date de dernière requête du client pour la ressource demandée. le jeu de caractères utilisé par le navigateur. la valeur du cookie associé au domaine auquel appartient la servlet demandée. le langage utilisé par le client.
Informations sur les entêtes de la requête import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; public class ServeurInfo extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); // on produit du texte ASCII PrintWriter out = res.getWriter(); out.println("Affichage des informations sur les en-têtes de la requête"); Enumeration entetes = req.getHeaderNames(); while (entetes.hasMoreElements()) { String nomEntete = (String) entetes.nextElement(); out.println("à l'entête " + nomEntete + " correspond la valeur" + getHeader(nomEntete) + "."); }
Informations sur le serveur import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class ServeurInfo extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); // on produit du texte ASCII PrintWriter out = res.getWriter(); out.println("Nom du serveur : " + req.getServerName() + " ."); out.println("Logiciel utilisé : " + req.getServletContext().getServerInfo() + " ."); out.println("Port du serveur : " + req.getServerPort() + " ."); out.println("Chemin vers le fichier " + req.getPathInfo() + " :" + req.getPathTranslated(req.getPathInfo()) + " ."); }
Construire la réponse : l'entête HttpServletResponse response On précise le type MIME de réponse avec response.setContentType (par exemple, "text/html") On peut préciser le statut de la réponse avec response.setStatus response.setStatus(HttpServletRequest.SC_NOT_FOUND) => erreur 404 res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY) avant de rediriger http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html pour voir tous les statuts possibles
Construire la réponse : le contenu Construction de HTML public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Etape 1. Spécifier le type MIME du contenu de la réponse response.setContentType("text/html"); // Etape 2. Récupère le PrintWriter // pour envoyer des données au client PrintWriter out = response.getWriter(); // Etape 3. Envoyer l’information au client out.println("<html>"); out.println("<head><title>Bonjour</title></head>"); out.println("<body>"); out.println("<h1> Bonjour à tous </h1>"); out.println("Il est : " + new java.util.Date()); out.println("</body></html>"); }
Construire la réponse : le contenu Construction de HTML orientée objet, avec weblogic par exemple (http://www.weblogic.com) import javax.servlet.*; import javax.servlet.http.*; import weblogic.html.*; import java.io.*; public class BonjourMondeObjet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); ServletPage page = new ServletPage(); page.getHead().addElement(new TitleElement("Bonjour !")); page.getBody().addElement(new BigElement("Bonjour !")); page.output(res.getOuputStream()); }
Construire la réponse : le contenu Renvoyer un fichier demandé en paramètre public class FileServer extends HttpServlet { // on peut être amené à envoyer des fichiers binaires, donc on utilise // un ServletOutputStream au lieu d'un PrintWriter ServletOutputStream out = res.getOutputStream(); // récupération d'une référence sur le fichier demandé File fichier = new File(req.getPathTranslated(); if (fichier == null) // si le fichier est introuvable on envoie un code d'erreur 404 res.sendError(HttpServletResponse.SC_NOT_FOUND); // sinon le type de contenu de la réponse correspond au type MIME // du fichier res.setContentType(getServletContext().getMimeType(fichier)); try { // on utilise un tampon de 4 ko pour lire le fichier // ce qui est plus rapide qu'un lecture ligne par ligne char[] tampon = new char[4 * 1024]; FileInputStream in = new FileInputStream(fichier); while (in.read(tampon) >= 0) { out.write(tampon); } } catch (IOEXception e) { // si une erreur se produit au milieu de la réponse // on envoie le code d'erreur HTTP adéquat res.sendError(HttpServletResponse.SC_PARTIAL_CONTENT); } finally { if (fichier != null) fichier.close();