10 juin 2009
Imprimer ce billet

Maven, jetty plugin et section mappée

Comme de nombreux projets web, nous utilisons pour le développement le plugin Jetty pour maven afin de lancer rapidement l'application.

Tout s'est toujours bien passé jusqu'au moment où nous avons attaqué concrètement la partie cliente (avec ses feuilles de style, fichiers Javascript...). En effet, le problème survient lors de la modification à la volée de ces fichiers pour n'avoir qu'un rafraîchissement à effectuer dans notre navigateur (et pas un arrêt/relance du serveur).

Ainsi, à la sauvegarde, on obtient l'erreur suivante :

Il nous est alors impossible de sauvegarder le fichier, l'erreur fichier ayant une section mappée utilisateur ouverte apparaît.

En effet, le file locking, mécanisme de restriction d'accès aux fichiers par les utilisateurs / processus, pose problème sous Windows si l'on veut modifier ces fichiers.

Jetty monte en mémoire tous les fichiers statiques (html, css, js, ...) et utilise pour cela une section mappée (memory mapped file) dans le cas d'un connecteur NIO (celui par défaut). Or, sous Windows, l'utilisation de ce segment de mémoire virtuelle provoque immédiatement un lock du fichier. Il devient donc impossible de modifier dynamiquement ces fichiers et nous sommes obligés de redémarrer notre Jetty pour prendre en compte toute modification.

Pour contourner ce problème, plusieurs solutions existent.

Changement de connecteur (NIO vers IO)

La plus simple et rapide est de modifier le connecteur par défaut (SelectChannelConnector) qui est de type NIO(contournement du problème mais après tout nous sommes en dev, pas besoin d'en faire des tonnes :) ).

Donc, dans notre pom.xml, nous allons modifier la configuration de notre maven-jetty-plugin en ajouter les quelques lignes suivantes :

<plugin>
   <groupId>org.mortbay.jetty</groupId>
   <artifactId>maven-jetty-plugin</artifactId>
   <configuration>
      ...
      <connectors>
         <connector implementation="org.mortbay.jetty.bio.SocketConnector">
            ...
         </connector>
      <connectors>
      ...
   </configuration>
</plugin>

Le contournement se situe au niveau de la balise connectors. Ainsi, on force Jetty à utiliser le SocketConnector au lieu du SelectChannelConnector par défaut. Ce connecteur non NIO n'utilisera pas de section mappée pour charger les fichiers statiques et pourra dès lors les modifier à notre guise sans redémarrer Jetty.

Webdefault.xml

L'autre solution, que l'on pourrait qualifier de plus officielle (car directement sur le site de codehaus) nous plonge dans le Jar pour modifier la configuration du fichier webdefault.xml (fichier chargé par Jetty avant le web.xml).

Ce fichier se trouve dans le Jar de Jetty (%M2_REPO%/org/mortbay/jetty/jetty/6.1.x/jetty-6.1.x.jar), au niveau du package org.mortbay.jetty.webapp. Il faut alors trouver la balise servlet où l'on peut lire

<!—
...
useFileMappedBuffer : If set to true (the default), a  memory mappedfile buffer
will be used to serve static content when using an NIO connector.
Setting this value to false means that a direct buffer will be used instead.
If you are having trouble with Windows file locking, set this to false.
...
-->

Cela tombe très bien, c'est exactement notre problème :-) . Nous pouvons donc passer cette valeur à false dans le fichier webdefault.xml pour ne pas utiliser cette fonctionnalité :

<servlet>
   ...
   <init-param>
      <param-name>useFileMappedBuffer</param-name>
      <param-value>false</param-value>
   </init-param> 
   ...
</servlet>

Il ne reste plus qu'une dernière manipulation : dire au maven-jetty-plugin d'utiliser notre fichier de configuration webdefault.xml. Ces quelques lignes feront ce travail (avec votre fichier au niveau de src/main/etc) :

<plugin>
   <groupId>org.mortbay.jetty</groupId>
   <artifactId>maven-jetty-plugin</artifactId>
   <configuration>
      ...
      <webDefaultXml>src/main/etc/webdefault.xml</webDefaultXml>
      ...
   </configuration>
</plugin>

Voilà donc un gain de productivité qui ne se refuse pas !