Les servlets A. Présentation La première question que l'on se pose lorsque l'on commence à développer des applications Web concerne en général l'aspect que peut avoir une servlet. En fait une servlet est une simple classe Java permettant d'ajouter des fonctionnalités à un serveur d'application. Pour que le serveur puisse prendre en charge cette nouvelle classe, celle-ci doit respecter certaines contraintes. Lorsqu'un client souhaite exécuter le code ajouté sur le serveur d'application, il doit en faire la demande au serveur par l'intermédiaire du protocole HTTP. Cette demande prend en général la forme d'une requête HTTP expédiée par le client à destination de l'url associée par le serveur à la servlet. Celle-ci exécute le traitement et génère une réponse HTTP pour transmettre au client le résultat de son exécution. 1. Dialogue avec une servlet Editions ENI - All rights reserved La base du dialogue entre un client et une servlet repose donc sur le protocole HTTP. Ce protocole est basé sur l'utilisation d'un couple requête-réponse. La requête est utilisée pour véhiculer l'information du client vers le serveur, la réponse étant bien sûr utilisée pour transporter de l'information du serveur vers le client. Les informations insérées dans la réponse HTTP sont en général liées aux résultats de l'exécution du code de la servlet. La création et l'expédition de la requête HTTP sont généralement confiées à un navigateur web. Celui-ci collecte les informations saisies par l'utilisateur par l'intermédiaire d'un document HTML puis effectue la mise en forme de ces informations en générant une requête HTTP. Cette requête est expédiée via un réseau à destination du serveur d'application. Le serveur réceptionne et analyse alors cette requête puis exécute la servlet concernée par la requête HTTP. La réponse HTTP est alors générée par le serveur puis expédiée vers le client toujours par l'intermédiaire du réseau. Le client (navigateur) réceptionne alors cette réponse et analyse son contenu afin de déterminer comment il doit l'interpréter (page HTML, image, son...). Le développement d'applications web avec JEE 6 45
Chapitre 3 Ce fonctionnement est résumé sur le schéma ci-dessous. 2. Traitements effectués Lors de la réception d'une requête HTTP par le serveur d'application, celui-ci transforme la requête HTTP, qui pour le moment est sous la forme d'une chaîne de caractères, en objet Java. Les informations contenues dans la requête HTTP sont transférées dans les propriétés de l'objet Java créé. Un objet Java représentant la réponse HTTP est également créé. Il est utilisé par la servlet pour construire la réponse qui sera ensuite renvoyée vers le client. Le serveur extrait ensuite de la requête le nom de la servlet qu'il doit exécuter. Le contrôle est alors passé à la servlet par l'appel de sa méthode service. Les objets représentant la requête et la réponse HTTP sont passés comme paramètres à la méthode service. Le principal travail de celle-ci consiste à déterminer le type de requête HTTP (GET, POST...). La dernière étape pour la servlet consiste à exécuter la méthode appropriée pour le traitement de ce type de requête (doget, dopost...). Ces méthodes reçoivent également les deux objets créés pour représenter la requête et la réponse HTTP. Le contenu de chacune de ces méthodes n'est pas figé mais sera redéfini par le concepteur de la servlet. Notre principal travail avec les servlets va donc consister à concevoir le contenu de ces méthodes. Dans la grande majorité des cas, ces méthodes vont extraire de la requête HTTP les paramètres reçus à partir du client, effectuer un traitement, puis construire la réponse. Lorsque le traitement de la servlet est terminé le serveur est à nouveau mis à contribution pour cette fois exécuter l'opération inverse de précédemment. L'objet représentant la réponse est transformé en réponse HTTP sous forme de texte puis celui-ci est retourné au client. Le traitement de la requête par la servlet est alors terminé. 46 Java Enterprise Edition
Les servlets 3. Classes et interfaces utilisées Editions ENI - All rights reserved Pour réaliser le traitement d'une requête HTTP, le serveur utilise de nombreuses classes et interfaces. Lorsque le serveur transforme la requête HTTP brute (sous forme d'une chaîne de caractères) en objet Java il utilise une classe qui implémente l'interface javax.servlet.http.httpservletrequest. Cette interface hérite elle-même de l'interface javax.servlet.servletrequest. L'objet obtenu permet ainsi d'accéder facilement aux principales informations véhiculées par la requête HTTP. Son utilisation est détaillée dans la section Utiliser la requête HTTP de ce chapitre. Un autre objet implémentant l'interface javax.servlet.http.httpservlet- Response, héritant de l'interface javax.servlet.servletresponse est également créé pour permettre la construction de la réponse HTTP par la servlet. L'utilisation de cet objet est détaillée dans la section Construire la réponse HTTP de ce chapitre. Pour que le serveur d'application puisse prendre en charge l'exécution de la servlet, celle-ci doit respecter certaines caractéristiques. Ces caractéristiques sont définies dans l'interface javax.servlet.servlet qui est implémentée par la classe abstraite javax.servlet.genericservlet. Cette classe est ensuite spécialisée par la classe javax.servlet.http.httpservlet définissant les caractéristiques d'une servlet pouvant être contactée grâce au protocole HTTP. C'est pratiquement toujours cette classe qui est utilisée comme classe de base pour les servlets. La section suivante détaille les principales méthodes disponibles pouvant être redéfinies dans cette classe. Le développement d'applications web avec JEE 6 47
Chapitre 3 B. Cycle de vie d'une servlet Comme n'importe quelle classe Java, une servlet doit être instanciée pour pouvoir être utilisée. Généralement lorsque vous avez besoin d'une instance de classe, vous utilisez l'opérateur new pour la créer. Le problème avec une servlet est que l'on ne sait pas exactement à quel moment on va en avoir besoin. Ce sont en fait les clients qui décident lorsqu'une instance de servlet est nécessaire en générant une requête HTTP pour demander son exécution. Le serveur d'application étant le mieux placé pour détecter cette demande, c'est donc lui qui est responsable de la création et de la destruction des instances de servlets. Pour ceci il utilise la stratégie suivante : Dès la réception d'une requête HTTP, il détermine si celle-ci concerne une servlet. Si c'est le cas, il vérifie s'il y a déjà une instance de cette servlet en mémoire, il appelle alors la méthode service de cette instance de servlet. Si aucune instance de cette servlet n'est disponible le serveur en crée une puis appelle la méthode init de cette nouvelle instance. Il peut alors ensuite appeler la méthode service de la servlet. Avec cette technique, une même instance de servlet va donc traiter les requêtes HTTP de très nombreux clients. Lorsque le serveur estime qu'il n'a plus besoin de cette servlet, il détruit automatiquement l'instance correspondante. Cette situation se produit en général uniquement lors de l'arrêt du serveur ou lorsqu'une nouvelle version de la servlet est disponible. Avant la suppression de l'instance d'une servlet la méthode destroy de celle-ci est exécutée. 1. Déclaration de la servlet Pour que le serveur puisse instancier la servlet, il doit bien sûr être informé de son existence. Celle-ci doit donc être déclarée dans le descripteur de déploiement de l'application. La déclaration suivante est à insérer dans le descripteur de déploiement pour chaque servlet de l'application. <servlet> <servlet-name>premiereservlet</servlet-name> <servlet-class>fr.eni.ri.premiereservlet</servlet-class> </servlet> 48 Java Enterprise Edition
Il est également conseillé d'ajouter une déclaration <servlet-mapping> afin de définir le ou les noms permettant de provoquer l'exécution de la servlet depuis une URL. <servlet-mapping> <servlet-name>premiereservlet</servlet-name> <url-pattern>/premiereservlet</url-pattern> <url-pattern>/firstservlet</url-pattern> </servlet-mapping> Cette déclaration est généralement utilisée pour éviter d'avoir à spécifier le nom du package lors de l'appel de la servlet. La version 3.0 des spécifications JEE concernant les servlets permet de remplacer ces deux déclarations par des annotations Java placées directement dans le code source de la servlet. Ces annotations sont analysées lors du déploiement de la servlet sur le serveur d'application. Les deux déclarations précédentes peuvent être remplacées par l'annotation suivante utilisée sur la classe de la servlet. @WebServlet(name="PremiereServlet", urlpatterns={"/premiereservlet", "/FirstServlet" }) public class PremiereServlet extends HttpServlet {... } D'autres annotations permettant la simplification du descripteur de déploiement sont répertoriées en annexe. 2. Méthode init Les servlets Editions ENI - All rights reserved La méthode init est appelée par le serveur aussitôt après l'instanciation de la servlet. Par défaut l'implémentation de cette méthode ne fait rien. Elle est en fait prévue pour permettre l'initialisation des ressources dont va avoir besoin la servlet pour exécuter ses traitements. Elle peut par exemple être utilisée pour établir une connexion vers un serveur de base de données ou ouvrir un fichier dans lequel la servlet va enregistrer des informations (journalisation d'informations). Si l'exécution de la méthode init ne se passe pas correctement une exception de type ServletException doit être déclenchée. Cette exception permet au serveur de détecter l'indisponibilité de la servlet. Le développement d'applications web avec JEE 6 49
Chapitre 3 3. Paramètres d'initialisation Il est possible d'éviter de placer directement dans le code certaines informations dont une servlet va avoir besoin pour son initialisation. Par exemple dans le cas où celle-ci a besoin d'établir une connexion vers un serveur de base de données les coordonnées du serveur ne seront connues qu'au moment du déploiement de l'application sur le serveur de production. Si ces informations sont inscrites directement dans le code, il faut obligatoirement modifier celui-ci et le recompiler lors du déploiement de l'application. Pour éviter cette manipulation, ce type d'information peut être placé dans le descripteur de déploiement de l'application (web.xml). La méthode init va pouvoir récupérer les informations à partir de ce fichier. Les informations d'initialisation de chaque servlet sont à ajouter à l'intérieur de la balise <servlet> par l'intermédiaire d'une balise <init-param>. Cette balise comporte deux éléments obligatoires : - <param-name> : représente le nom du paramètre. Ce nom doit être utilisé dans la méthode init de la servlet pour accéder au paramètre. - <param-value> : représente la valeur affectée au paramètre. L'élément <description> est quant à lui optionnel mais très utile pour la clarté du descripteur de déploiement. La déclaration d'une servlet dans le fichier web.xml prend donc la forme suivante : <servlet> <servlet-name>premiereservlet</servlet-name> <servlet-class>fr.eni.ri.premiereservlet</servlet-class> <init-param> <description> adresse du serveur de base de données </description> <param-name>adresseipbdd</param-name> <param-value>127.0.0.1</param-value> </init-param> </servlet> Il est également possible de déclarer les paramètres d'initialisation d'une servlet en utilisant les annotations. @WebServlet(name"PremiereServlet", urlpatterns={"/premiereservlet", "/FirstServlet" }, initparams={@webinitparam(description="adresse du serveur de base de données", name="adresseipbdd", value="127.0.0.1")}) 50 Java Enterprise Edition