Les Servlets Présentation Cycle de vie Principe de fonctionnement Échange d’informations Gestion d’une session
Présentation des servlets Exécution de traitements sur un serveur suite à une requête d’un client Prévu pour tout type de protocole avec une implémentation pour HTTP Génération de pages dynamiques orientée sur une logique de traitement Concurrent aux technologies : CGI NSAPI / ISAPI La servlet effectue un traitement en réponse à une requête client. Sur un protocole HTTP, les servlets s'exécutent dans un serveur Web. Elles publient un résultat sur le flux de réponse HTTP. Cela peut être des pages dynamiques générées en HTML ou en XML.
Le principe Présentation Processus Java qui s’exécute côté serveur suite à une requête d’un client Exploite les paramètres de la requête Repose sur les classes de l’API servlet de l'environnement J2EE S’exécute au sein d'un moteur de servlets couplé au serveur Web Contrairement à l’applet, la servlet permet d’avoir tous les droits d’un programme Java classique puisque c’est du côté serveur qu’elle s’exécute. Il n’y a donc aucun problème de sécurité du côté client. Les packages dédiés aux servlets sont les packages javax.servlet.* Sun s’appuie désormais sur Tomcat (un projet du groupe Apache) pour fournir un moteur de servlet de référence. Auparavant, Sun s’appuyait sur le JSWDK (Java Server Web Development Kit). L'API servlet ne fait pas partie de l'environnement J2SE, donc ses classes ne sont pas dans le JDK. Il faut utiliser l'environnement J2EE.
Fonctionnement Client Response Web Server Request Moteur de servlets Présentation Client Web Server Moteur de servlets Request Response Application Java Fonctionnement : La requête d’un client est envoyée vers le serveur Web (HTTP) qui la redirige vers le gestionnaire de servlets. Le gestionnaire de servlets exécute la requête. Une servlet fonctionne comme un programme java classique, c’est-à-dire qu’elle peut communiquer avec d’autres applications au niveau du serveur. Après exécution, la réponse du serveur est renvoyée au client via le serveur Web.
Moteurs de servlets Présentation Les moteurs peuvent être directement intégrés aux serveurs Web ou ajoutés comme modules par la suite Parmi tous les serveurs supportant les servlets, on peut noter : Tomcat, Resin, JRun, BEA Weblogic Server, IBM Websphere, Oracle Application Server, ... Par exemple, dans le cas de Tomcat (moteur de servlets préconisé par Sun), on peut utiliser Tomcat seul, il fonctionne en moteur de servlet et en serveur HTTP On peut coupler Tomcat avec un autre serveur HTTP (Apache par exemple) Liste non exhaustive de moteurs de servlets existants: Allaire : http://www.allaire.com/ Apache Tomcat : http://jakarta.apache.org/ BEA Weblogic : http://www.beasys.com Resin : http://www.caucho.com IBM Websphere : http://www.software.ibm.com/webservers/appserv/ Iplanet : http://www.iplanet.com/products/infrastructure/web_servers/iws/index.html Oracle: http://www.oracle.com/appserver/ Inprise : http://www.inprise.com Enhydra : http://www.enhydra.org Orion : http://www.orionserver.com
Cycle de vie d’une servlet Chargement et initialisation Serveur Servlet Serveur Échange de données avec les clients Client Servlet Cycle de vie : Le serveur charge la servlet et l’initialise. Il est possible de paramétrer le chargement pour qu’il ait lieu dès le démarrage du serveur ou bien à la première requête du premier utilisateur sur cette servlet. Les clients envoient des requêtes au serveur qui les transmet à la servlet. Pour chaque requête HTTP, la servlet doit fournir une réponse qui sera retournée au client. Le serveur supprime la servlet. Remarque : Dans la pratique, le chargement ne sera fait qu’une fois et le déchargement une fois: lorsque l’on décide de couper le service. Cependant, il est possible de faire plusieurs réglages: par exemple charger et décharger la servlet à chaque requête ou bien la décharger après un certain temps d’inactivité. Déchargement Serveur Servlet
Affectation de la requête Conteneur de servlet Cycle de vie Conteneur de servlet Thread Servlet init() initialisation Requête HTTP Affectation de la requête service() exécution du service Réponse HTTP Cycle de vie d'une servlet : le conteneur crée une instance de la servlet à la première invocation de celle-ci. il initialise la servlet (méthode init()) lorsqu'il reçoit une requête associée à la servlet, il appelle la méthode service() avant de supprimer la servlet, il appelle la méthode destroy() l'instance est détruite, elle sera éliminée par le GC En réalité, le conteneur ne gère pas un thread par servlet mais un pool de threads. destroy() nettoyage
Mise en oeuvre (1) Mise en oeuvre Le package javax.servlet contient des interfaces et des classes pour l’écriture de servlets Une servlet hérite de la classe HTTPServlet qui elle-même implémente l’interface Servlet La servlet peut redéfinir des méthodes de HTTPServlet qui correspondent à des requêtes HTTP classiques : service(), doGet(), doPost() ... Pour qu’une classe soit une servlet, il suffit qu’elle implémente l’interface Servlet. Cependant, l’utilisation classique est l’héritage de la classe HTTPServlet qui est déjà définie dans le package javax.servlet.http. Cette classe est une implémentation de l'interface Servlet pour le protocole HTTP. Exemples de méthodes de la classe HttpServlet : La méthode doGet() correspond à la requête GET du protocole HTTP qui effectue une demande simple de documents au serveur. La méthode doPost() correspond à la requête POST du protocole HTTP qui correspond au postage d'un formulaire de saisie sur le serveur. Il existe d’autres méthodes répondant à des services HTTP comme doDelete() ou doPut().
Mise en oeuvre (2) Mise en oeuvre Les méthodes de type doGet() et doPost() reçoivent en paramètre deux objets : Un objet HttpServletRequest qui encapsule la requête du client (renseignée automatiquement par le moteur) Un objet HttpServletResponse qui encapsule la réponse qui sera retournée au client (à renseigner dans le code de la servlet) Les méthodes de type doGet() ou doPost() sont appelées par le serveur en fonction de la requête du client. Le corps de la méthode effectue le traitement attendu sur les données utilisateurs et renvoie la réponse. Il peut être redéfinit par l’utilisateur.
Ex 3 public class ExempleServlet extends HttpServlet { /** * Réponse à une requête HTTP GET : retourne une simple page HTML */ public void doGet ( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { PrintWriter out; String titre = "Exemple simple de document généré par une servlet"; // Renseigne la réponse sur le type du document retourné response.setContentType( "text/html" ); // Récupère un flux sur la réponse afin d’y écrire la page out = response.getWriter(); // Ecriture de la chaîne correspondant à la page HTML retournée out.println("<HTML><HEAD><TITLE>"); out.println( titre ); out.println("</TITLE></HEAD><BODY>"); out.println("<H1>" + titre + "</H1>"); out.println("<P>Cette page provient d’une servlet."); out.println("</BODY></HTML>"); out.close(); } Mise en oeuvre Ex 3 Le résultat de l’appel à cette servlet par une requête HTTP affiche une page HTML simple ayant pour titre « Exemple simple de document généré par une servlet » et pour corps « Cette page provient d’une servlet ». Remarque : Écriture des liens vers la servlet. Lors de l’appel de la servlet, on va produire du HTML dans le flux de sortie (réponse). Généralement, ce HTML va contenir des liens vers cette même servlet (ex : lien retour …). On peut utiliser des informations accessible depuis la servlet pour construire dynamiquement ces liens (évite de coder des liens en dur) : Interface HttpServletRequest : getContextPath() getPathTranslated() getQueryString() getRequestURI() getServletPath()
Surcharge des méthodes Mise en oeuvre void init() GenericServlet Appelée au chargement de la servlet : initialisation, lecture des fichiers de conf… void service() Appelée à chaque invocation de la servlet void destroy() Appelée au déchargement de la servlet : destruction de tous les objets créés En fonction du type de requête du client, il est possible de surcharger également les méthodes suivantes de la classe HTTPServlet (extrait de la javadoc javax.servlet) : protected void doDelete(HttpServletRequest req, HttpServletResponse resp): Called by the server (via the service method) to allow a servlet to handle a DELETE request. protected void doGet(HttpServletRequest req, HttpServletResponse resp) : Called by the server (via the service method) to allow a servlet to handle a GET request. protected void doOptions(HttpServletRequest req, HttpServletResponse resp) : Called by the server (via the service method) to allow a servlet to handle a OPTIONS request. protected void doPost(HttpServletRequest req, HttpServletResponse resp) : Called by the server (via the service method) to allow a servlet to handle a POST request. protected void doPut(HttpServletRequest req, HttpServletResponse resp) : Called by the server (via the service method) to allow a servlet to handle a PUT request. protected void doTrace(HttpServletRequest req, HttpServletResponse resp) : Called by the server (via the service method) to allow a servlet to handle a TRACE request. protected void service(HttpServletRequest req, HttpServletResponse resp) : Receives standard HTTP requests from the public service method and dispatches them to the doXXX methods defined in this class.
Partage des données Servlet Mise en oeuvre Chaque servlet n’est instanciée qu’une seule fois, il y a donc persistence des données entre deux appels de la servlet La valeur d’un attribut de la classe dérivant d’HttpServlet dépend des autres invocations (multi-threads) => Il ne faut pas utiliser les attributs pour stocker des informations ! Exemple : public class Compteur extends HttpServlet { int compte = 0; public void service (ServletRequest request, ServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html><body>"); compte++; out.println("<h1>"+compte+"</h1>"); out.println("</body> </html>"); } } Si un utilisateur 1 fait une requête sur cette servlet, elle affichera 1. Si un utilisateur 2 fait une requête sur cette servlet, elle affichera 2. Si l’utilisateur 1 refait une requête sur cette servlet, elle affichera 3. En réalité, ce résultat apparaît si les appels sur la servlet sont bien séquentiels. Le problème, c'est que ces appels peuvent être effectués en parallèle dans des threads différents et le comportement de la variable compte non synchronisée peut être perturbé par ces accès multi-threads. Il est donc fortement déconseillé de faire persister des informations dans les attributs d'une servlet car cela nécessite de mettre en œuvre un mécanisme de synchronisation des appels. Ce qui remet en cause l'intérêt des servlets… Servlet Client 1 Client 2 Client 4 Client 3
Echange d’informations Trois objets peuvent servir d’intermédiaires pour échanger des informations entre composants Web : La requête La session L’« application » Ils se comportent comme des hashtables et disposent des méthodes : setAttribute() getAttribute()
La requête Echange d'informations La requête est passée en paramètre de la méthode service() (ou doGet(), doPost()…) On y récupère l'URL d’appel, les paramètres HTTP et la session utilisateur getRequestURL() getParameter(String name) getSession() Elle peut être transmise à une autre servlet lors d’une délégation de traitement (forward) On y place des objets servant à la génération de la réponse L’objet requête est un objet de la classe HttpServletRequest. La délégation de traitement est un mécanisme qui permet à une servlet de demander à une deuxième servlet de réaliser la suite du traitement et de produire le résultat. La requête existe jusqu’à ce qu’une réponse soit renvoyée au client. Elle est généralement transmise lors de la délégation du traitement (forward ou include). On peut alors l'utiliser pour y passer temporairement certaines valeurs intéressantes pour la génération de la réponse. Par exemple : public class ExempleServlet extends HttpServlet { /** Réponse à une requête HTTP GET : * retourne une simple page HTML */ public void service (HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { request.setAttribute(”monAttribut”, ”maValeur”); }
L’« application » Echange d'informations L’« application » est un objet de la classe ServletContext récupéré à partir de la servlet L’« application » est partagée entre tous les objets Web et tous les clients d’une même application Web On y place les objets servant pour toute l’application (ressources…) ServletContext application = this.getServletContext(); L’objet application est un objet de la classe ServletContext
La session La session peut être récupérée à partir de la requête Echange d'informations La session peut être récupérée à partir de la requête La session est liée à un client. Elle existe tant qu’elle n’a pas été détruite volontairement ou par un time out On y place les objets servant durant toute la connexion du client (typiquement : un caddie) HttpSession session = request.getSession(); Remarque : Un gestionnaire de servlet fonctionne avec un serveur HTTP. Or le protocole HTTP ne connait pas directement la notion de session client (protocole non connecté). Pour identifier chaque session au niveau du gestionnaire de servlet, il est donc indispensable d’utiliser soit les cookies soit un identifiant de la session codé dans les urls. L'implémentation par défaut repose sur le principe des cookies. L'encodage d'URL Nécessite des modifications dans le code Java. L’objet session est un objet de la classe HttpSession. Attention à ne pas stocker des objets trop gros dans les sessions, cela peut influer sur les performances !
Les cookies Permettent le stockage d’informations au niveau du client Gestion de sessions Permettent le stockage d’informations au niveau du client Utilisation de la classe javax.servlet.http.Cookie Création Récupération Cookie monCookie = new Cookie("nom", "valeur"); response.addCookie(monCookie); Les cookies sont couramment utilisés pour « pister » un client, savoir d’où il vient, quand il vient. Attention ! L’utilisateur peut interdire leur dépôt dans son navigateur. Cookie[] mesCookies = request.getCookies();
URL Rewriting Gestion de sessions Gestion des sessions pour des clients n’acceptant pas les cookies Méthodes HttpServletResponse.encodeURL() HttpServletResponse.encodeRedirectURL() Utilise le session ID pour retrouver la session de l’utilisateur out.println( "<tr>" + ... + "<a href=\"" + response.encodeURL(monLien) + "\"> + books[i].getTitle() + "</a> ); Par défaut, la gestion des sessions utilise les cookies. Pour permettre de gérer les sessions où le browser ne supporte pas ou rejette les cookies, on peut utiliser l'URL rewriting. Quand on utilise l’ URL rewriting, on appelle des méthodes qui incluent le session ID dans l’URL. Il faut appeler ces méthodes à chaque envoi de réponse de la servlet pour l’écriture de tous les liens. Soit une servlet CatalogServlet qui retourne 2 liens pour chaque livre à l’utilisateur. Ces deux liens doivent être réécrits. Quand l’utilisateur clique sur un lien réécrit, la servlet reconnaît le sessionID. Ensuite, la méthode getSession utilise ce sessionID pour récupérer l’objet HTTPSession de l’utilisateur. Si l’utilisateur clique sur un lien non-réécrit, alors la session est perdue. La servlet contactée par ce lien non–réécrit va créer une nouvelle session, mais cette nouvelle session ne contiendra pas les informations contenues dans la session perdue. Lorsqu’une session est perdue par une servlet, elle est perdue pour toutes les servlets qui la partageaient.
Déléguer des traitements Gestion de sessions Possibilité de rediriger la requête HTTP sur une autre URL Possibilité de déléguer une partie de la réponse à une autre ressource : Par inclusion : include() Par délégation complète : forward() response.sendRedirect(<location>); Ex 4-6 Le sendRedirect() impose au client de reposter la requête HTTP sur une autre URL. Cette URL peut être relative à la racine du conteneur (si elle commence par "/") ou relative à l'URL de la servlet courante. L'URL passée en paramètre est exprimée sous la forme d'une chaîne de caractères. Il est dans ce cas impossible de stocker des valeurs dans la requête HTTP car l'objet qui la représente n'est pas réutilisé. L'appel à include() permet d'inclure le contenu d'une ressource, statique ou généré (servlet, JSP ou page HTML) dans la réponse. Mais la servlet d'origine conserve la main et gère la production de la réponse complète. L'appel à forward() permet de transmettre les objets de requête et de réponse à une autre ressource (servlet, JSP ou HTML) qui prend en main la gestion de la réponse. Ceci reste cependant transparent pour l'utilisateur qui ne voit que l'URL de la servlet d'entrée. ServletContext ctx = getServletContext(); RequestDispatcher rd; rd = ctx.getRequestDispatcher(<RelativeURLOfJSP>); rd.include( <RequestObject>, <ResponseObject> ); rd.forward( <RequestObject>, <ResponseObject> );