14 juin 2010

Configuration d'une application JavaEE

Nicolas Romanetti m'a fait l'autre jour une remarque sur un manque dans mon livre Apache Maven concernant le chapitre sur JavaEE : Dans le modèle JavaEE (pré-6), on met les classes et les ressources dans un WAR, lui même dans un EAR, et on balance tout ça sur le serveur. Où placer la configuration de l'application ?

Si on lit la spec JavaEE, elle prévoit un rôle d'assembleur d'application, qui va prendre l'EAR, éditer ses fichiers de déploiement XML pour faire le lien entre les ressources de son serveur (DataSource, files JMS, ...) et l'application. Personnellement je n'ai jamais rencontré ce cas de figure, et mes clients attendent plutôt une application prête à fonctionner avec au plus un fichier de configuration properties externe à l'EAR.

Option 1 : embarquer la conf en fonction de l'environnement cible

Si on veut suivre l'esprit JavaEE on peut utiliser des profils Maven pour packager dans le WAR les ressources associées à l'environnement cible :

<profile>
      <id>dev</id>
      <build>
        <resources>
          <resource>
            <directory>${basedir}/src/env/dev</directory>
          </resource>
        </resources>
      </build>
    </profile>
    <profile>
      <id>prod</id>
      <build>
        <resources>
          <resource>
            <directory>${basedir}/src/env/prod</directory>
          </resource>
        </resources>
      </build>
    </profile>

il suffit donc d'ajouter un petit "-Pprod" pour générer l'application dans sa configuration de production. Par contre, la configuration ne peut pas être éditée simplement une fois l'application installée.

Option 2 : passer par le JNDI


L'autre option respectant l'esprit JavaEE est de se baser sur les ressources JNDI, et donc d'accéder à une donnée d'environnement de type URL ou String qui pointe vers notre fichier de properties. Dans le web.xml j'ajoute donc un petit :

<env-entry>
      <description>Chemin de la configuration locale</description>
      <env-entry-name>appli.home</env-entry-name>
      <env-entry-type>java.lang.String</env-entry-type>
      <env-entry-value>/valeur/par/defaut</env-entry-value>
    </env-entry>

Je n'ai plus qu'à définir cette ressource dans le JNDI de mon serveur d'application, ce qui va par contre être spécifique à chaque serveur - Tomcat par exemple ne permet pas de définir une ressource URL, alors que Websphère le supporte (pas classique de dire du bien de celui-là, n'est ce pas ?).

Pour y accéder, il faut faire un lookup JNIDI ce qui n'est pas léger léger, et si on veut passer par Spring et ses PlaceHolder ${truc} on regardera du côté de SPR-3030.

Option 3 : passer par une propriété système


Dernière option, la version "light" : dans de nombreux cas, je constat que le serveur d'application n'héberge qu'une seule instance de l'application, aussi on peut positionner des variables systèmes au niveau de la JVM pour lui passer un paramètre, typiquement notre appli.home. Et là, rien de spécial à faire pour activer le configuration Spring en dehors d'activer le SYSTEM_PROPERTIES_MODE_OVERRIDE.
Dans le même esprit, on peut externaliser la configuration log4j en ajoutant un -Dlog4j.configuration=file:///monlog4j.xml.

Perso je préfère cette dernière variante, la plus légère à mettre en oeuvre et la plus simple à expliquer aux développeurs. JNDI reste un truc assez mystérieux, qui plus est avec des variations pour sa configuration en fonction des serveurs d'application. L'accès au DirContext se fait trop souvent via un mauvais copier-coller qui référence les classes du serveur d'application (alors que justement le but est de s'en découpler !) et/ou en ajoutant au petit bonheur la chance des java:comp/env ... peut être le sujet d'un autre billet :)

Option 4: compléter le classpath du serveur

Suite au commentaire de Sylvain François, une 4ème option, comparable au system properties : ajouter au Classpath de l'application le chemin d'un répertoire contenant les fichiers de conf. En jouant sur l'ordre de chargement du ClassPath on peut ainsi surchager les fichiers de conf "par défaut" présents dans le WAR/EAR.

Ces deux dernière options sont assez comparables et leur mise en ouvre va dépendre de la liberté qu'on a pour configurer le serveur d'application. Certaines équipes d'exploit' donnent la main sur la commande de lancement de la JVM, d'autres préfèrent la blinder mais laissent libre accès aux répertoires de libs ...

Qu'en pensez vous ? Donnez votre avis : http://doodle.com/d4qdxc87w76i524c