Il y a 14 ans -
Temps de lecture 6 minutes
Servlet 3.0, les 3 points marquants
Servlet 3.0 est une révision importante des spécifications, elle apporte son lot de nouveautés : simplification, pluggabilité, support de l’asynchrone, sécurité et d’autres modifications mineures. Cette JSR, passée en final draft en mai denier, fera partie des nouveautés apportées par Java EE 6 dont la sortie ne devrait pas tarder. Mais que se cache t-il derrière cette nouvelle version ? Simple dépoussiérage après 161 JSR d’écart depuis la dernière version 2.5 ou véritables avancées ? C’est la question à laquelle nous allons essayer de répondre au cours de cet article en nous focalisant sur les 3 points marquants de cette nouvelle version :
- La configuration via annotations, parce que c’est tendance
- Les Web Fragments, un bon moyen de modulariser
- L’exécution asynchrone, pour les architectures Comet
La configuration via annotations, c’est tendance
L’épidémie continue ! Je ne parle pas de la fameuse grippe qui circule en ce moment … mais des annotations. C’est la première nouveauté que je voulais mettre en avant concernant les Servlets 3.0. Dorénavant, vous pourrez configurer vos Servlets, Filtres et autres ContextListeners via des annotations spécifiques. Vous en êtes fan ? Tant mieux. Pour ma part, je n’utilise directement les servlets que très rarement, ce n’est donc pas cette nouveauté qui m’intéresse le plus. Pour avoir déjà eu cette discussion avec des collègues, cette réponse engendre rapidement des exclamations du type : « Tout le monde ne fait pas que du Spring !». Et pourtant, c’est un fait : cela fait bon nombre d’années que je n’ai pas eu besoin de créer de servlets, Spring ou non. Et vous, quels usages en faites vous ?
D’autre part, de manière générale, il ne faut pas dénigrer les bons vieux fichiers XML. S’ils peuvent paraître lourds et verbeux, ils ont l’avantage de centraliser les différentes déclarations et configurations à un seul endroit. Il ne me semble d’ailleurs pas complètement burlesque de découpler un mapping de son objet cible : les annotations sont avant tout destinées à des méta-données statiques.
Cet aparté mis à part, revenons au cœur du sujet : les annotations disponibles dans Servlet 3.0 :
- @WebServlet, permet de marquer une classe comme servlet. Cette classe doit toujours étendre HttpServlet
@WebServlet(name="simpleservlet", urlPatterns="/myservlet", "/simpleservlet"}) public class SimpleServlet extends HttpServlet { ... }
- @WebFilter, de la même manière, cette annotation permet de déclarer un Filtre
@WebFilter(urlPatterns={"/mufilter","/simplefilter"}) public class SimpleFilter implements Filter { ... }
- @WebInitParam, cette annotation est utilisée pour préciser des paramètres d’initialisation aux Servlets ou aux Filtres
@WebServlet(name="simpleservlet", urlPatterns="/myservlet", "/simpleservlet"}, initParams={ @WebInitParam(name="param1", value="value1"), @WebInitParam(name="param2", value="value2") }) public class MyServlet extends HttpServlet { ... }
- @WebListener, permet d’annoter un ContextListener pour vous permettre de recevoir certains événements de la WebApp
@WebListener public class MyListener implements ServletContextListener { ... }
- @MultipartConfig, sur une Servlet, cette annotation indique que la requête attendue est du type mime/multipart et son contenu est rendu directement disponible par l’intermédiaire de la méthode
request.getParts()
… enfin ! :)
Vous l’avez compris, avec l’introduction des annotations, le descripteur de déploiement web.xml devient optionnel. La configuration est simplifiée, c’était le but recherché, mais rien de révolutionnaire dans cette fonctionnalité.
Outre les annotations, les spécifications fournissent un autre nouveau moyen d’ajouter des Servlets et Filtres. Une API permet la configuration dynamique au Runtime lors des différents évènements récupérés par le ContextListener. Pour quoi faire ? Pour permettre aux Frameworks de s’initialiser dynamiquement à partir de fichier de configuration ? Dans les faits, l’intérêt est limité puisque l’ajout de Servlet passe par l’intermédiaire d’un simple nom de classe (qui sera chargé par un Class.forName() ). Il vous sera donc difficile de passer des valeurs dynamiques à celle-ci avant son initialisation. Du coup, on perd l’intérêt d’un tel mécanisme.
context.addServlet("name", "description", "fr.xebia.web.servlet.SimpleServlet", initParams, -1, false); context.addServletMapping("name", urlPatterns); context.addFilter("filterName", "description", "fr.xebia.web.filter.SimpleFilter", initParams, false);
Les Web Fragments, un bon moyen de modulariser
Les web fragments fournissent un moyen simple de partitionner logiquement le descripteur de déploiement (web.xml). Ainsi, différents frameworks (Struts, JSF …) pourront fournir par ce biais une configuration Web par défaut directement depuis leur Jar. Ils faciliteront et masqueront ainsi au maximum leur configuration à leurs utilisateurs.
La déclaration d’un fragment est simple. Il s’agit d’un fichier XML conventionné qui doit :
- se nommer web-fragment.xml
- être placé dans le répertoire META-INF d’un Jar
- avoir comme élément racine du XML une balise
<web-fragment>
Pendant le déploiement, le conteneur est responsable de scanner, découvrir et traiter les différents fragments répondant à ces critères.
< !-- Exemple de web-fragment.xml -->XebiaCustomFragment fr.xebia.web.servlet.CustomListenerInFragment CutomServletInFragment fr.xebia.web.servlet.CutomServletInFragment
Il vous est possible de nommer vos fragments par l’intermédiaire d’une balise spécifique <name />
. Cette balise vous permet de contrôler qu’un fragment ne soit chargé qu’une seule et unique fois. D’autres balises : <absolute-ordering />
, <ordering />
, <before />
, <after />
, <others />
permettent de contrôler l’ordre de déclaration des différents fragments.
XebiaCustomFragment2 ... XebiaCustomFragment
Il y a fort à parier que les frameworks web stars adopteront rapidement cette nouveauté pour faciliter la vie de leurs utilisateurs. Les tambouilles internes de configuration se feront plus discrètes tout en accentuant la modularité de vos projets. Pourtant ces fragments seront-ils d’une réelle utilité ? Pour ma part, je pense que cette fonctionnalité est une belle avancée. Elle incite les développeurs à regrouper leur code et configuration dans un même endroit. Il s’agit donc, si on peut dire, d’une première étape vers un développement modulaire.
L’exécution asynchrone, pour les architectures Comet
Je vous ai gardé le meilleur pour la fin, c’est à mon avis la nouvelle fonctionnalité la plus remarquable : l’ajout de l’exécution asynchrone. Jusqu’à présent, lorsqu’un utilisateur appelait une servlet, il n’avait d’autre choix que d’attendre la fin du traitement avant de pouvoir reprendre la main : l’exécution d’une Servlet était synchrone. Ce comportement était-il désiré ou subi ? Probablement un peu des deux. Lorsque vous vouliez lancer un traitement et rendre la main rapidement à vos utilisateurs, vous deviez gérer par vous-même le fonctionnement asynchrone : pools de threads, files d’attente, listeners … bref, le début des galères. À quoi bon garder cette glue technique pour un besoin aussi simple que récurrent ?
Servlet 3.0 vous propose donc un mécanisme d’exécution asynchrone vous permettant de simplifier ce code à son strict minimum. Configuré dynamiquement par le code request.startAsync()
ou via les propriétés asyncSupported
et asyncTimeOut
des annotations présentées précédemment, il est possible de rendre le traitement d’une requête asynchrone, avec si besoin, un timeout. Pour cela, une nouvelle API permet de suspendre et de reprendre le traitement d’une requête en cours d’exécution et d’activer ou non la réponse en fonction des besoins de l’application. Il est également possible d’ajouter des listeners qui recevront les notifications de fin de traitement ou de timeout. Ce nouveau mécanisme vous sera particulièrement utile si vous désirez mettre en place une architecture du type Comet dont la caractéristique principale est de laisser ouvert le plus longtemps possible un canal de communication entre le client et le serveur (dans l’attente de nouveautés sur le serveur).
@WebServlet("/servletAsync", asyncSupported=true) public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) { // Ajouter un listener request.addAsyncListener(new MyAsyncListener()) // Definition du timeout programatiquement request.setAsyncTimeout(10000); // Execution asynchrone et récupération du context AsyncContext context = request.startAsync(request, response); } }
Commentaire
10 réponses pour " Servlet 3.0, les 3 points marquants "
Published by Michael , Il y a 14 ans
Intéressant :). Concernant les annotations pour les servlets, je serais curieux de savoir s’ils se sont mis d’accord sur la façon dont les classes sont scannées. La dernière fois que j’avais regardé, il était question que tout le classpath soit scanné (aka. tous les fichiers .class ouverts) pour trouver les annotations @WebServlet. Pas terrible pour les perfs au démarrage…
A titre de comparaison, dans Spring on conseille d’affiner au maximum le paramétrage du component-scan.
Published by Benoît , Il y a 14 ans
Bonjour, un certain nombre de points suscitent mon incompréhension. J’aimerais avoir votre avis sur le sujet.
1. Configuration des servlets par des annotations.
Je vois vraiment pas l’intérêt. Les annotations servent à regrouper des « meta-informations » avec le code pour que tout ce qui est relatif à un élément fonctionnel se retrouve au même endroit, mais dans le cas d’éléments de configuration, on veut justement séparer le code de la configuration. Qu’elle est donc l’utilité de permettre de mettre les valeurs des paramètres d’initialisation par exemple.
De plus, comme l’indique Michael, la totalité des classes du classpath n’est pas chargée lors d’un démarrage. Comment le serveur d’application saura-t-il qu’il doit charger les classes comprenant les annotations indiquant qu’il s’agit de servlets ?
Rq : Très bonne nouvelle pour le support de l’extraction des entity-body multipart en natif.
2. Web Fragments
Sur le principe, j’abonde à 100%, par contre, au niveau de la prise en compte d’un nouveau fragment, je ne comprend pas la logique : le xml fragment doit être dans le META-INF d’un jar : c’est-à-dire que la configuration de la servlet (valeurs des paramètres d’initialisation, servlet-mapping…) devra être spécifiée avant le build : donc prévue lors du développement et pas du déploiement !!!
C’est étrange pour de la configuration.
Et quelle galère pendant le développement : on devra faire un build du jar pendant les développements et le test des servlets à déclarer dans un fragment !
Il aurait été tellement simple de prévoir un répertoire WEB-INF/web-fragments/ dans lequel on aurait mis des fichiers XML avec un espace de nommage (exemple : WEB-INF/web-fragments/org/apache/monprojet/messervlets/monweb-fragment.xml).
De plus, le fait de mettre le fragment dans le jar créé une adhérence entre la déclaration d’une classe et son emplacement dans la classpath. Que faire si, pour des raisons de génie-logiciel, on veut définir les servlets dans un autre emplacement dans le classpath (WEB-INF/classes/, voir les répertoires de lib du serveur d’application) ?
Merci de votre avis.
Published by Frédéric , Il y a 14 ans
Concernant les web-fragments : sera-t-il possible « d’exclure » un web-fragment.xml présent dans un jar ?
Je vois d’ici arriver les problèmes de « collisions » entre frameworks dûes à des url-pattern qui se marchent les unes sur les autres …
J’ose espérer que la définition d’une url-pattern dans le web.xml prévaut sur celles des web-fragment.xml :-)
Pour l’exécution asynchrone : il serait intéressant de voir ce que fait cette classe MyAsyncListener() … en regardant le code tel quel, on n’a pas vraiment l’impression de lancer des choses en parallèle ;-)
En tous les cas de bonnes choses dans cette nouvelle spec !.. A-t-on une idée des serveurs d’appli qui sont, a ce jour, « compliant » d’ores et déjà avec la norme ?
Published by Michael Figuiere , Il y a 14 ans
@Benoît : Typiquement, les frameworks et serveurs d’application qui ont besoin de scanner les annotations sur les classes préfèreront s’appuyer sur une solution de lecture directe des fichiers .class par une librairie de manipulation de bytecode (ASM, Javassist,…) plutôt que de recourir au mécanisme d’introspection standard de Java qui nécessite que la JVM charge la classe, comme l’explique Bill Burke (project lead JBoss) dans Scanning Java Annotations at Runtime. JBoss propose d’ailleurs une API permettant de faire ce scan simplement dont nous parlions récemment : JBoss Annotations.
Cordialement,
Michael Figuière (Xebia)
Published by Benoît , Il y a 14 ans
@Michael Figuiere : merci pour cette réponse (je bookmarque les liens !). Cela veut dire que, dans ces cas de figures, les serveurs d’application vont recréer quelque part (dans un répertoire cache interne) un descripteur de déploiement à partir des annotations.
Je gage que ce serait une source de bug, comme c’est déjà fréquemment le cas avec les caches (caches des JSP compilées, caches de fichiers de conf par webapp – cf conf/Catalina/localhost/manager.xml …) qui ne s’invalident pas quand il faut.
Published by Antonio Goncalves , Il y a 14 ans
Mardi 13 octobre nous aurons l’occasion de découvrir tous les secrets de Servlet 3.0 au Paris JUG (http://www.parisjug.org/xwiki/bin/view/Meeting/20091013). Remy Bauchat (http://www.parisjug.org/xwiki/bin/view/Speaker/MaucheratRemy) viendra présenter les nouveautés de la spécification et répondre à vos questions. Je vous propose même d’utiliser la mailing list du JUG (http://www.parisjug.org/xwiki/bin/view/Main/MailingList) pour lister les questions.
Published by Erwan Alliaume , Il y a 14 ans
@Michael (I.) & (F.) : Je n’ai pas vu de moyen de fine tuner ce scan, par contre il est possible de désactiver complètement les features de scan d’annotations et de web-fragments (via un attribut « metadata-complete »)
@Benoit : Je partage ton avis sur les annotations et les WebInitParam, cela dit c’est une feature comme une autre… À partir du moment où ils ont décidé de proposer un tel mécanisme, c’est normal qu’ils mettent sur le tapis un équivalent à tout ce qu’on pouvait trouver dans le web.xml. L’intérêt aurait été renforcé avec une intégration des Expression Langage dans les valeurs des annotations (comme dans Spring 3), mais je n’ai rien vu de la sorte dans les specs de la jsr.
@Freferic : Sauf erreur de ma part, la reference implementation des Servlet 3 est Glassfish 3, Tomcat et Jetty sont également cités en contributeurs de la JSR mais ne s’étendent pas sur le sujet pour le moment
@Antonio : C’est cool que tu fasses venir ce genre de speaker :)
Erwan (Xebia)
Published by Alexis MP , Il y a 14 ans
Je confirme que GlassFish v3 est la RI de servlet 3.0. On a passé le code freeze, donc c’est fonctionnel.
Les derniers promoted builds sont ici: http://download.java.net/glassfish/v3/promoted/