Servlet 3.0, les 3 points marquants

Article publié par Erwan Alliaume le 15 septembre 2009.

Catégorie(s) : Java / JEE

 

9 commentaires »

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, 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 -->
<web-fragment>
  <name>XebiaCustomFragment</name>
  <listener>
    <listener-class> fr.xebia.web.servlet.CustomListenerInFragment</listener-class>
  </listener>
  <servlet>
    <servlet-name>CutomServletInFragment</servlet-name>
    <servlet-class>fr.xebia.web.servlet.CutomServletInFragment</servlet-class>
  </servlet>
</web-fragment>

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.

<web-fragment>
  <name>XebiaCustomFragment2</name>
  <ordering><after>XebiaCustomFragment</after></ordering>
  ...
</web-fragment>

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);
   }
}

twitter erwan alliaume

9 réponses à “Servlet 3.0, les 3 points marquants”

  1. Michael dit :

    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.

  2. Benoît dit :

    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.

  3. Frédéric dit :

    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 ?

  4. @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)

  5. Benoît dit :

    @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.

  6. 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.

  7. @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)

  8. Alexis MP dit :

    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/

  9. [...] noter que la norme Servlet 3 amènera son lot d’évolutions sur cet aspect en incluant un mécanisme d’exécution asynchrone (défini par annotation sur la classe d’implémentation de la [...]

Laisser un commentaire

 

Page optimized by WP Minify WordPress Plugin