SESSION 3 Introduction aux servlets Programme de la session ✔ Principes de fonctionnement des servlets ✔ Création d'une première servlet minimale ✔ Les servlets et les pages JSP résultantes Avant de nous plonger dans les détails de la rédaction d'une page JSP, il sera profitable de connaître les grandes lignes du fonctionnement des servlets, car ces petits programmes exécutés côté serveur constituent le soubassement de JSP. Une servlet est un programme écrit en langage Java et capable de répondre à une requête HTTP, tout comme le font les programmes CGI (Common Gateway Interface), actuellement très répandus. A la différence du CGI, une servlet peut profiter des caractéristiques évoluées du langage Java, comme l'exécution distribuée multithread (un processus peut lancer l'exécution de plusieurs sous-processus simultanés) et la notion de session (permettant de conserver les valeurs des données d'une session utilisateur d'une requête à l'autre). Chaque page JSP que vous rédigerez finira par produire une servlet Java capable de recevoir une requête HTTP et de renvoyer une réponse HTTP. 30 JSP Web Training Principes de fonctionnement des servlets Une servlet, (prononcez servlette) tout comme une applet (prononcez applette), est un programme écrit en langage Java. Une applet est un programme qui s'exécute sur la machine du client (le visiteur) après avoir été rapatriée (téléchargée) depuis le site du serveur ; l'exécution est réalisée sous le contrôle de la machine virtuelle JVM (Java Virtual Machine) intégrée au navigateur. Une applet ne comporte pas obligatoirement de partie visuelle, mais presque toutes en ont une. La taille n'est pas limitée mais, dans la pratique, les applets restent de taille réduite pour ne pas entraîner des temps de téléchargement inacceptables pour le visiteur. En revanche, une servlet n'est jamais téléchargée ; elle réside et est exécutée sur le serveur. Vous pouvez considérer les servlets comme une infrastructure pour les applications Web se servant de Java. Une servlet a accès à toutes les fonctions des interfaces de programmation API de Java ; elle est donc indépendante de la plate-forme d'exécution et peut aisément servir à enrichir les capacités fonctionnelles de la plupart des logiciels serveurs Web. Une servlet se distingue d'une application Java classique de par ses capacités à gérer les requêtes HTTP. Une telle requête est émise par le navigateur du client suite au clic d'un lien hypertexte, à l'envoi d'un formulaire, ou encore à la saisie d'une adresse URL dans un champ approprié. Pour d'autres détails au sujet de HTTP, voyez le Chapitre 19. Lorsqu'un serveur Web reçoit une requête HTTP, il analyse la chaîne d'adresse URL demandée pour savoir quel type de page a été demandé par le navigateur. Si le navigateur a demandé une servlet, le serveur Web transmet la requête au conteneur de servlets. Le programme conteneur gère la création, l'utilisation et la destruction des servlets, soit le cycle de vie de ces servlets. Le conteneur de servlets que nous allons utiliser tout au long de ce livre est Tomcat (installé lors du Chapitre 2). Pour chaque demande d'exécution d'une servlet via une requête HTTP, le conteneur charge la servlet en mémoire et crée un objet instance à partir de la classe nommée Servlet. Le conteneur n'a plus ensuite qu'à Session 3 - Introduction aux servlets 31 lancer l'exécution de la servlet en l'initialisant par appel à la méthode init() de la partie interface nommée Servlet, tout en lui transmettant un objet ServletConfig en paramètre. L'objet ServletConfig contient des informations de configuration sous forme de paires nom/valeur ; ces valeurs sont reçues et exploitées par la servlet. Une fois la servlet correctement initialisée (la méthode init() s'est terminée sans erreur), la servlet peut traiter la requête HTTP et générer sa réponse HTTP. La méthode service() transmet les deux objets ServletRequest et ServletResponse à la servlet. Sauf mention contraire explicite, le conteneur de servlets travaille en mode multithread (sousprocessus multiples), ce qui lui permet de gérer plusieurs requêtes en parallèle, en distribuant les requêtes à la servlet sous forme de sous-processus (threads) distincts, comme le montre la Figure 3-1. Figure 3-1 Une même instance de servlet sait gérer plusieurs requêtes. L'autre mode de fonctionnement consiste à utiliser l'interface nommée SingleThreadModel. Dans ce cas, les requêtes sont mises en file d'attente pour être exécutées l'une après l'autre (Figure 3-2). 32 JSP Web Training Figure 3-2 Avec l'interface SingleThreadModel, la servlet répond aux requêtes l'une après l'autre. Une fois toutes les requêtes en attente servies, le conteneur de servlets appelle la méthode destroy() pour libérer l'espace mémoire de l'objet servlet devenu inutile. Dès l'arrivée d'une nouvelle requête concernant lamême servlet, le processus reprend au début. Création d'une première servlet minimale Le Listing 3-1 propose de créer une servlet minimale qui se contente d'afficher un message dans la fenêtre du navigateur. Servez-vous des instructions suivantes pour compiler et exécuter ce premier exemple. Session 3 - Introduction aux servlets 33 Listing 3-1 La servlet SalutMonde.java affiche un message. /** SalutMonde (traduit de HelloWorld) **/ import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SalutMonde extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<HTML>"); out.println("<HEAD>"); out.println("<TITLE>"); out.println("Exemple de servlet minimale SalutMonde"); out.println("</TITLE>"); out.println("</HEAD>"); out.println("<BODY>"); out.println("<H1>Salut à tout le monde</H1>"); out.println("</BODY>"); out.println("</HTML>"); } } 1. Saisissez le code source ci-dessus dans un éditeur de texte et enregistrez le fichier dans le sous-répertoire <Tomcat>\webapps\ ROOT\WEB-INF\classes sous le nom SalutMonde et avec l'extension de nom de fichier .java. Ce fichier .java servira de source pour la compilation de la servlet. Vous pouvez aussi récupérer le fichier dans le sous-répertoire sources\jsc_03 du CD-ROM. 2. Avant de compiler le fichier .java pour produire un fichier .class, vérifiez que la variable CLASSPATH comporte bien une mention relative au fichier servlet.jar fourni avec Tomcat, fichier qui est nécessaire à la compilation. Si vous avez par exemple installé Tomcat dans C:\JAKARTA, CLASSPATH doit comporter une chaîne 34 JSP Web Training C:\JAKARTA\lib\servlet.jar. Revoyez le Chapitre 2 si nécessaire en ce qui concerne la définition des variables d'environnement. 3. Ouvrez une fenêtre de ligne de commande (Commandes MS-DOS sous Windows) et utilisez des commandes DOS pour aller dans le sous-répertoire des classes du serveur Tomcat (<Tomcat>\webapps\ ROOT\WEB-INF\classes). Saisissez alors la ligne de commande de compilation suivante : javac SalutMonde.java Si aucune erreur n'apparaît, la servlet est compilée. Si une erreur survient, vérifiez d'abord que vous n'avez pas fait de faute de frappe dans le programme source ou sur la ligne de commande. Vérifiez la variable CLASSPATH. Une fois la compilation achevée, listez le contenu du répertoire ; vous devez trouver un fichier nommé SalutMonde.class. C'est la servlet compilée. 4. S'il a été lancé, arrêtez et redémarrez Tomcat. (lancez shutdown.bat puis startup.bat du sous-répertoire <Tomcat>\bin). 5. Il suffit maintenant de démarrer votre navigateur et de préciser le mot "servlet" après le nom de domaine pour indiquer à Tomcat que nous désirons solliciter le conteneur de servlets. Pour accéder à notre servlet (en supposant que Tomcat utilise le port 8080), saisissez l'adresse suivante : http://localhost:8080/servlet/SalutMonde N'ajoutez surtout pas l'extension de nom .class. Vous devriez obtenir ce que montre la Figure 3-3. La première partie de SalutMonde.java réunit les instructions d'importation. Le paquetage nommé java.io contient l'objet PrintWriter, qui sera appelé plus tard lors de l'exécution. Les deux autres paquetages, javax.servlet.* et javax.servlet.http.*, offrent le support de l'interface API. Nous vous ferons remarquer que javax.servlet.http.* se trouve en fait incorporé à javax.servlet.* (défini d'abord). Ce paquetage requiert pourtant une instruction import spécifique, car l'interface API des servlets sait gérer d'autres servlets en plus de celle correspondant au protocole HTTP. Session 3 - Introduction aux servlets 35 Figure 3-3 Résultat de l'exécution de la servlet minimale. Vous aurez remarqué que les deuxième et troisième paquetages commencent par javax et non java. Les servlets ne font en effet pas partie des classes Java fondamentales définies pour ce langage ; ce sont des classes spécialisées qui viennent en extension des classes fondamentales, d'où le x ajouté. La seconde partie du programme correspond aux déclarations de classes. La servlet SalutMonde dérive de la classe HttpServlet, et en hérite donc toutes les méthodes et propriétés. C'est le cas de la plupart des servlets : elles dérivent de la classe HttpServlet définie dans le paquetage javax.servlet.http. La déclaration de classe contient la déclaration de méthode doGet(), qui se charge de traiter les requêtes GET reçues des navigateurs Web. Cette méthode doGet() est définie dans la superclasse HttpServlet ; elle attend deux paramètres d'entrée et sait déclencher deux exceptions. Les deux paramètres sont HttpServletRequest et HttpServletResponse. Leur valeur est générée par le moteur JSP lorsqu'il reçoit chaque requête HTTP. Une fois appelés, les objets deviennent accessibles à la servlet. L'objet HttpServletRequest contient des informations concernant la requête HTTP émise par le navigateur, alors que l'objet HttpServletResponse contient celles relatives aux réponses HTTP. 36 JSP Web Training La classe dérivée (sous-classe) SalutMonde surcharge (redéfinit) la méthode doGet(), dont elle hérite, en y ajoutant les traitements dont elle a besoin. Dans notre exemple, la méthode recueille un identificateur handle pointant sur l'objet PrintWriter, ce qui lui permet de se servir de cet objet pour envoyer un document HTML vers le flux de sortie. A chaque ligne commençant par out.println(, du contenu HTML est envoyé dans le flux de sortie. La servlet retransmet ces données vers le navigateur, et la page Web apparaît. Les servlets et les pages JSP résultantes Une page JSP correspond à une servlet prêt à être exécutée. Lorsque le navigateur réclame une servlet et non une page JSP (c'était le cas pour SalutMonde.java), le conteneur de servlets réceptionne la requête et la transmet à la servlet qui a d'abord été compilée. Dans le cas d'une page JSP, il n'est plus nécessaire de compiler le code source ; le simple fait de la demander force le conteneur JSP Tomcat à compiler la page à la volée. Dans le prochain exemple, nous allons voir ce qui se passe avec une page JSP. Commencez par saisir (ou récupérez dans le sous-répertoire sources\jsc_03 du CD) le programme SalutMonde.jsp et stockez le fichier dans le sous-répertoire <Tomcat>\webapps\ROOT. Cette page élémentaire insère la chaîne "Salut à tout le monde" dans une expression à afficher. <%-- Fichier SalutMonde.jsp --%> <html> <head> <title>Premier source en JSP</title> </head> <body> <h2><%= "Salut à tout le monde" %></h2> </body> </html> Tomcat étant activé, ouvrez votre navigateur et saisissez l'adresse suivante : Session 3 - Introduction aux servlets 37 http://localhost:8080/SalutMonde.jsp Patientez quelques secondes avant de voir apparaître la page. Ce délai est dû au fait que Tomcat a constaté qu'il ne disposait pas d'une version compilée de la page JSP. Il a donc d'abord créé un fichier source Java à partir du fichier JSP, puis il a compilé ce fichier .java pour obtenir un fichier .class exécutable. C'est le fichier compilé qui a généré le code HTML qui a été renvoyé au navigateur. Lors de vos prochaines visites de la même page, l'accès est quasi instantané. Tomcat stocke les deux fichiers qu'il génère quelque part dans la sousstructure de répertoires <Tomcat>\work. Pour vérifier la présence des deux fichiers, utilisez l'Explorateur pour aller jusqu'à ce répertoire work. Cherchez le sous-répertoire localhost_8080 (ce qui correspond à l'adresse de la racine ROOT du serveur). Vous devriez trouver deux fichiers portant un nom assez long, mais basé sur celui du fichier .jsp (contenant SalutMonde quelque part). Le fichier .java est le code source Java et le fichier .class est le fichier exécutable. Le Listing 3-2 montre le contenu du fichier .java. La partie imprimée en gras correspond à la partie générant le code HTML. Listing 3-2 Le code source SalutMonde.jsp produit ce code source Java (_0002fSalutMonde_0002ejspSalutMonde_jsp_0.java). import import import import import import import import import import import import javax.servlet.*; javax.servlet.http.*; javax.servlet.jsp.*; javax.servlet.jsp.tagext.*; java.io.PrintWriter; java.io.IOException; java.io.FileInputStream; java.io.ObjectInputStream; java.util.Vector; org.apache.jasper.runtime.*; java.beans.*; org.apache.jasper.JasperException; public class _0002fSalutMonde_0002ejspSalutMonde_jsp_0 extends HttpJspBase { static { 38 JSP Web Training Listing 3-2 Le code source SalutMonde.jsp produit ce code source Java (_0002fSalutMonde_0002ejspSalutMonde_jsp_0.java). (...) } public _0002fSalutMonde_0002ejspSalutMonde_jsp_0( ) { } private static boolean _jspx_inited = false; public final void _jspx_init() throws JasperException { } public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { JspFactory _jspxFactory = null; PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; String _value = null; try { if (_jspx_inited == false) { _jspx_init(); _jspx_inited = true; } _jspxFactory = JspFactory.getDefaultFactory(); response.setContentType("text/html;charset=8859_1"); pageContext = _jspxFactory.getPageContext(this, request, response, "", true, 8192, true); application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); // HTML // begin [file="C:\\jakarta\\webapps\\ROOT\\SalutMonde.jsp";from=(0,24);to=(7,7)] out.write("\r\n\r\n<html>\r\n <head>\r\n <title>Premier source en JSP</title>\r\n </head>\r\n <body>\r\n <H2>"); // end Session 3 - Introduction aux servlets 39 Listing 3-2 Le code source SalutMonde.jsp produit ce code source Java (_0002fSalutMonde_0002ejspSalutMonde_jsp_0.java). (...) // begin [file="C:\\jakarta\\webapps\\ROOT\\SalutMonde.jsp";from=(7,10);to=(7,35)] out.print( "Salut à tout le monde" ); // end // HTML // begin [file="C:\\jakarta\\webapps\\ROOT\\SalutMonde.jsp";from=(7,37);to=(10,0)] out.write("</H2>\r\n </body>\r\n</html>\r\n"); // end } catch (Exception ex) { if (out.getBufferSize() != 0) out.clearBuffer(); pageContext.handlePageException(ex); } finally { out.flush(); _jspxFactory.releasePageContext(pageContext); } } } Cet exemple montre bien le formidable avantage de JSP. En effet, Tomcat génère la totalité du code Java de la servlet, soit plus de soixante-dix lignes, à partir d'un code source JSP de moins de dix lignes. Si vous effectuez des modifications de votre fichier .jsp et ne voyez pas les modifications prises en compte, arrêtez et redémarrez Tomcat. RÉVISION ● La technologie des servlets constitue le soubassement de celle de JSP. ● Un conteneur de servlets sert à gérer le cycle de vie des servlets. ● Les servlets multithread savent gérer plusieurs requêtes en parallèle. 40 JSP Web Training ● Les servlets se basant sur l'interface SingleThreadModel traitent les requêtes l'une après l'autre. ● La méthode doGet() prend en charge le traitement des requêtes HTTP et le renvoi des réponses HTTP. ● Le conteneur JSP compile un fichier de page JSP en servlet .class via l'étape intermédiaire du fichier .java. TESTEZ VOS CONNAISSANCES 1. Qu'est-ce qu'une requête HTTP ? 2. Quel est l'usage de l'interface SingleThreadModel ? 3. Quels objets sont transmis en paramètres de la méthode init() de la servlet ? 4. Quelle méthode sert à supprimer une servlet devenue inutile de la mémoire ? 5. Quels paquetages doit-on importer pour supporter les servlets ?