Externalisez la configuration de vos webapps Spring et sauvez un chaton!

Bien externaliser la configuration de votre application est la clé qui ouvre les portes de l’automatisation des déploiements, du déploiement continu, des astreintes du dimanche sans appels des équipes de production, des transferts de connaissance de fin de mission sans lendemains, etc. À chaque fois qu’une application Web est déployée sur un environnement alors que sa configuration n’est pas externalisée, un chaton meurt ; externaliser vos conf est donc également une bonne action envers tous les chatons du monde.

Dans cet article, je vais commencer par rappeler rapidement les stratégies de reconfiguration que l’on peut croiser dans ce monde sauvage avec leurs avantages et leurs inconvénients. Je présenterai ensuite en détail une de ces stratégies, appliquée à une application web basée sur Spring. Les principes de cette stratégie peuvent être résumés par cette citation :

One WAR to package them all, One WAR to serve them all,
One WAR to deploy them all and in JNDI bind them.

Les stratégies de configuration

Il existe plusieurs stratégies de configuration des applications en fonction de leur environnement de déploiement. Elles peuvent toutes être décrites par l’une des approches suivantes :

  • Configuration manuelle,
  • Duplication du livrable,
  • Duplication de la configuration dans le livrable,
  • Externalisation totale.

Configuration manuelle

La stratégie de configuration manuelle consiste à ouvrir le livrable et à changer manuellement les valeurs de configuration qui doivent être adaptées à un nouvel environnement de déploiement.

Cette stratégie est extrêmement risquée :

  • Elle implique l’intervention de l’être humain pour effectuer sans erreurs des tâches très répétitives. Même s’il ne s’agit que de copier-coller un fichier dans le bon dossier puis de refaire le package, le risque d’erreur est présent.
  • L’information de la manipulation à réaliser peut être facilement perdue lors d’un départ et sa rétro-analyse sera sans doute très complexe.
  • La personne qui effectue la manipulation pour les environnements sécurisés doit avoir les habilitations correspondantes au risque d’exposer le SI.
  • Le livrable testé sur chaque environnement est différent. Le package validé en recette ne sera pas celui monté en production. Difficile de mettre en place des contrôles de signature et de promotion de packages dans ces conditions.
  • Toute modification d’environnement nécessite de réouvrir le livrable, de faire le changement à la main et de le repackager.

Cette approche doit être évitée autant que possible. Elle peut éventuellement être justifiée en tout début de projet à condition d’être très vite remplacée par une stratégie industrialisée.

Duplication du livrable

Étant donné les outils de build modernes (maven, sbt, gradle, etc.), il est relativement simple de mettre en place un processus de build répétable. Il suffit alors de paramétrer le processus pour qu’il embarque la configuration de l’environnement cible.

Cette stratégie est déjà plus sûre que la précédente mais elle présente quand même des défauts importants :

  • Le livrable déployé dans chaque environnement sera différent. Il ne sera pas possible d’utiliser la signature du livrable de test pour identifier le livrable de production.
  • Toute modification du livrable implique le repackaging et la relivraison.
  • Les processus de builds sont généralement mis en œuvre par les développeurs qui ont ainsi accès aux paramétrages de production (logins, mots de passe, etc.).

Duplication de la configuration dans le livrable

Si le livrable contient toutes les configurations possibles, il ne reste qu’à lui faire passer une propriété pour qu’il puisse sélectionner la configuration adaptée. Faire passer une unique propriété est assez simple, que ce soit par une variable d’environnement ou par une propriété Java passée en argument de la JVM.

Il reste deux défauts :

  • Toute modification d’environnement nécessite un repackaging et une relivraison.
  • Puisque toutes les configurations sont dans le package, les développeurs ont nécessairement accès à la configuration de production (logins, mots de passe, etc.).

Externalisation totale

Il existe deux variantes pour cette stratégie. La première stratégie se base sur une variable d’environnement ou sur une propriété Java pour faire passer le chemin vers la configuration à l’application. Cette approche est à privilégier pour les applications de type client lourd ou les batchs du fait de sa simplicité. Elle est moins adaptée à des webapps qui vont potentiellement partager leur JVM, leur serveur et donc le namespace de l’environnement avec d’autres applications.

La seconde variante utilise des binding JNDI pour découpler l’application de sa configuration. Cette seconde approche est à privilégier pour les webapps. Elle ne nécessite pas de modification des scripts de démarrage du serveur, les configurations de différentes applications peuvent facilement être mutualisées ou isolées en fonction des besoins. C’est cette approche que je vous propose de mettre en œuvre maintenant.

Étude de cas : Une webapp basée sur Spring

Pour l’exercice, nous allons reprendre une webapp triviale créée pour l’occasion. Le code source de son état initial est disponible sur github. Dans cette version initiale, les dépendances de l’application sur l’environnement sont paramétrées dans des fichiers de configuration embarqués dans le livrable final. Plus précisément, nous y trouverons le nom de l’environnement actuel et les paramètres de connexion à la base de données.

État initial

La déclaration à la base de données est faite avec la forme classique que l’on peut retrouver dans les applications exemples de spring :

  • src/main/webapp/WEB-INF/applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." >
  <!-- DATASOURCE DEFINITION -->
  <context:property-placeholder location="classpath:jdbc.properties"/>
  <bean id="dataSource"
          class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close"
          p:driverClass="${jdbc.driverClassName}"
          p:jdbcUrl="${jdbc.url}"
          p:user="${jdbc.username}"
          p:password="${jdbc.password}"/>

</beans>

Le fichier de properties correspondant :

  • src/main/resources/jdbc.properties:
jdbc.driverClassName=org.hsqldb.jdbc.JDBCDriver
jdbc.url=jdbc:hsqldb:file:/tmp/xweb-dev.db
jdbc.username=sa
jdbc.password=

Pour la servlet Dispatcher de Spring, nous n’allons déclarer qu’une propriété permettant d’afficher l’environnement dans lequel on se trouve. On pourrait imaginer que notre application utilise des webservices pour lesquels il faut spécifier des serveurs différents en fonction de l’environnement de déploiement. La dépendance au fichier de properties se trouve ligne 3 :

  • src/main/webapp/WEB-INF/SpringDispatcher-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." >
    <context:property-placeholder location="classpath:env.properties"/>
    <context:annotation-config/>
    <context:component-scan base-package="fr.xebia.xweb"/>
    <mvc:annotation-driven/>
    <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

Le fichier de properties:

  • src/main/resources/env.properties:
env=dev

Externaliser la connexion à la base de données

L’externalisation de la connexion à la base de données est l’étape la plus simple à mettre en œuvre car elle est pleinement supportée dans Spring depuis longtemps (présent en standard depuis Spring 2.0). Le code source de cette étape est disponible sur github sous forme d’un commit séparé. Voici comment nous allons procéder :

  • Déclarer la connexion à la base comme une ressource JNDI,
  • Configurer Spring pour utiliser la ressource JNDI.

Déclarer la connexion comme une ressource JNDI

Notre application web en mode développement s’exécute dans Jetty. Déclarons une source de données de niveau conteneur. Pour cela, créons un fichier de configuration pour le conteneur :

  • src/dev/jetty-config.xml:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
    <Array id="plusConfig" type="java.lang.String">
        <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
        <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
        <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
        <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
        <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item><!-- add for jndi -->
        <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item><!-- add for jndi -->
        <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
        <!-- not needed for jetty-8 -->
        <!-- <Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item> -->
    </Array>

    <New id="dataSource" class="org.eclipse.jetty.plus.jndi.EnvEntry">
        <Arg>jdbc/dataSource</Arg>
        <Arg>
            <New class="com.mchange.v2.c3p0.ComboPooledDataSource">
                <Set name="driverClass">org.hsqldb.jdbc.JDBCDriver</Set>
                <Set name="jdbcUrl">jdbc:hsqldb:file:/tmp/xweb-dev.db</Set>
                <Set name="user">sa</Set>
                <Set name="password"></Set>
            </New>
        </Arg>
        <Arg type="boolean">true</Arg>
    </New>
</Configure>

La section Array permet de configurer les modules du serveur qui seront activés. Il faut ajouter EnvConfiguration et PlusConfiguration pour activer le registre JNDI. Ensuite, nous créons une datasource enregistrée sous le nom jdbc/dataSource. C’est exactement la même datasource que celle qui était configurée à travers le fichier jdbc.properties précédement. Elle sera disponible avec l’URL complète java:comp/env/jdbc/dataSource.

Nous pouvons vérifier que notre datasource est bien enregistrée dans le contexte en démarrant l’application et en inspectant le contenu du contexte :

\__/
\__/jdbc/
\__/jdbc/dataSource => org.eclipse.jetty.plus.jndi.EnvEntry
jdbc/
jdbc/dataSource => javax.naming.Reference

Il ne reste plus qu’à configurer Spring pour utiliser notre nouvelle datasource.

Configurer Spring pour utiliser une datasource JNDI

Spring support nativement ce type de configuration. Il suffit de changer notre fichier applicationContext.xml comme suit :

  • src/main/webapp/WEB-INF/applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." >
    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/dataSource"/>
</beans>

Votre application utilise désormais une datasource hébergée dans JNDI. Le même WAR peut être déployé sur n’importe quel serveur déclarant une datasource sous le nom jdbc/dataSource et trouver sa connexion à la base de données.

Externaliser les fichiers properties de configuration

Il existe trois possibilités pour l’externalisation des properties :

  • Déclarer un nom JNDI pour chaque paire clé-valeur.
  • Déclarer un objet de type Properties qui contient la liste des paires.
  • Déclarer un nom JNDI pour le chemin d’accès à un fichier de properties.

La première approche peut être efficace s’il n’y a pas trop de paires à déclarer, dans le cas contraire cette technique va rapidement aboutir à une pollution du contexte JNDI. La seconde consiste à déclarer un objet de type Properties dans le namespace. À moins de disposer d’une factory permettant de créer cet objet à partir d’un fichier de properties existant, il faudra redéclarer toutes les paires dans la déclaration de cet objet. Nous allons donc explorer la troisième solution qui nous permet de simplement copier notre fichier existant à un emplacement sur le serveur et d’en référencer le chemin dans JNDI.
Une fois cette étape réalisée, il restera à le consommer dans Spring. La version 3.1 de ce dernier offre de nouveaux mécanismes de résolution des properties qui rendent cette approche particulièrement concise, nous verrons ensuite une variante pour les autres versions de Spring. Le code est, là encore, disponible sous la forme d’un commit spécifique sur Github.

Déclarer le chemin du fichier de properties dans JNDI

Nous allons ici déclarer la valeur dans un contexte JNDI spécifique à la webapp. Pour cela, nous allons devoir déclarer un fichier de contexte de niveau webapp auprès de Jetty :

  • src/dev/jetty-xweb.xml :
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="xweb" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="envPropertiePath" class="org.eclipse.jetty.plus.jndi.EnvEntry">
        <Arg>envPropertiePath</Arg>
        <Arg type="java.lang.String">file:///Users/jean/dev/jdev/src/work/art_externalize_conf/xweb/src/main/resources/env.properties</Arg>
        <Arg type="boolean">true</Arg>
    </New>
</Configure>

L’inspection de notre contexte JNDI avec la page dumpjndi.jsp montre notre nouvelle ressource de type String ainsi que sa valeur, prête à être consommée :

\__/
\__/jdbc/
\__/jdbc/dataSource => org.eclipse.jetty.plus.jndi.EnvEntry
\__/envPropertiePath => org.eclipse.jetty.plus.jndi.EnvEntry
jdbc/
jdbc/dataSource => javax.naming.Reference
envPropertiePath => java.lang.String[file:///path/to/xweb/src/main/resources/env.properties]

Configurer Spring 3.1 pour utiliser un chemin fourni par JNDI

Spring 3.1 offre un nouveau mécanisme de gestion des variations de contexte applicatif en fonction de l’environnement. Le mécanisme est assez complexe avec entre autre un système de profils, mais il offre une fonctionnalité qui nous interesse : celle d’être capable d’utiliser une source JNDI pour faire de la substitution de placeholder dans le reste de la configuration. Il est donc possible d’écrire <bean attribute=${java:comp/env/name}.../> dans un fichier XML. Nous n’allons pas explorer les profils de configuration car ils s’approchent plus d’une implémentation de type duplication de configuration dans le livrable.

Dans le cas d’une webapp, le contexte créé par Spring contient automatiquement un DefaultWebEnvironment. Ce type d’environement déclare par défaut une JNDIPropertySource. Nous pouvons donc simplement configurer notre PropertyPlaceHolder comme suit :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." >
    <util:properties id="myprops" location="${java:comp/env/envPropertiePath}"/>
    <context:property-placeholder properties-ref="myprops"/>
    <context:annotation-config/>
    <!-- ... -->
</beans>

Notre objectif est atteint : l’application web utilise désormais les ressources qui lui sont données par le serveur d’application pour se configurer.

Variante pour les versions de Spring avant 3.1

Pour configurer une application dans une version de Spring précédant la version 3.1, il est nécessaire de réaliser quelques ajustements. Mais le principe reste le même. Le code de cette variante est disponible sur github.

Le mécanisme de transformation en objet properties est un peu différent. Il faut changer le chemin du fichier de configuration (ligne 7) :

  • src/dev/jetty-xweb.xml:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="xweb" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="envPropertiePath" class="org.eclipse.jetty.plus.jndi.EnvEntry">
        <Arg>envPropertiePath</Arg>
        <Arg type="java.lang.String">/path/to/xweb/src/main/resources/env.properties</Arg>
        <Arg type="boolean">true</Arg>
    </New>
</Configure>

Il faut ensuite faire en sorte que Spring puisse l’utiliser :

  • src/main/webapp/WEB-INF/SpringDispatcher-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." >
  <bean id="placeholderProperties"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
        <bean class="org.springframework.core.io.FileSystemResource">
          <constructor-arg>
            <jee:jndi-lookup id="envPropertiePath" jndi-name="envPropertiePath"/>
          </constructor-arg>
        </bean>
      </list>
    </property>
  </bean>
  <context:annotation-config/>
  <!-- ... -->
  </bean>
</beans>

La propriété locations de la classe PropertyPlaceholderConfigurer a la signature suivante :

private Resource[] locations;

Malheureusement, le résultat de notre résolution JNDI est une String. Il faut donc la convertir en Resource. C’est le résultat obtenu en passant la chaîne en argument au constructeur de la classe utilitaire FileSystemResource de Spring.

Notre objectif est là encore atteint : l’application web utilise désormais les ressources qui lui sont données par le serveur d’application pour se configurer.

Conclusion

Nous avons démontré qu’il est relativement facile de construire un livrable, ici un WAR, complètement découplé de sa configuration par le biais du contexte JNDI. Les avantages de ce type de livrables sont nombreux, aussi bien pour les équipes de développement que pour les équipes d’exploitation.

Pour les développeurs, déjà, cela signifie que le package exécuté en dev est le même que celui exécuté en intégration, en recette et finalement en production. Pas besoin pour eux de gérer d’innombrables profiles maven pour tous les environnements de déploiement, pas besoin non plus de jouer avec les répertoires de sources pour inclure les bons fichiers de configuration dans le classpath. Il y a juste à configurer une fois le serveur de développement pour qu’il expose les fichiers de configuration dans son contexte. Il devient alors possible d’automatiser totalement les builds et les livraisons au niveau d’un serveur d’intégration continue.

Les équipes d’exploitation gagnent en liberté pour la gestion de leur parc de serveurs. Si une base de données ou un serveur de webservices doit changer de machine, il suffit de changer les fichiers de configuration des applications dépendantes et de redémarrer les instances. Pas besoin de demander aux équipes de développement de préparer une version et de la faire recetter par les équipes de test. Tout le monde gagne du temps. Elles gagnent aussi en sécurité : pas besoin de donner aux équipes de développement les logins et mots de passe de la base de données de production par exemple.

Certaines actions à la croisée des périmètres de ces deux équipes s’en trouvent simplifiées. Un bug survient en production ? Il suffit de mettre à jour le fichier de configuration du framework de log. Dans le pire des cas, il faudra aussi redémarrer l’instance, dans le meilleur, le framework détectera la modification et activera les niveaux de logs demandés. La documentation de livraison de votre application devient triviale : il suffit de déposer le WAR, aucune manipulation manuelle. Les équipes de production peuvent facilement le scripter. En cas d’ajout de variable de configuration, elles peuvent être déclarées par les études et positionnées en avance de la livraison. Si une variable n’a pas la bonne valeur, pas besoin d’annuler la livraison, il est simple de la corriger et de relancer l’application.

Au delà de ces problèmes courants, vous pouvez imaginer gagner en vitesse et en réactivité dans vos livraisons, jusqu’à passer en déploiement continu avec des mécanismes de promotion de package d’un environnement à l’autre. Le package étant binairement identique vous pouvez l’identifier grâce à une signature.

N’attendez plus pour sauver des chatons, externalisez vos configurations !

Liens utiles

14 commentaires

  • Article intéressant, je suis curieux de savoir comment Java 7 va résoudre cette problématique dans le multi-tenancy.

    A noter le projet Apache Commons configuration qui favorise l’externalisation des properties (dont une implémentation JNDI) : http://commons.apache.org/configuration/index.html.

  • Merci pour cet article détaillé, je ne connaissais pas cette nouvelle possibilité de Spring 3.1.

    Malheureusement, et sauf erreur de ma part, mon Weblogic 11 ne semble pas permettre d’ajouter des entrées dans l’arbre JNDI … sauf à les embarquer dans les descripteurs spécifiques packagés dans les archives de déploiement.

  • Tres bon article ! C’est effectivement fondamental pour tout process d’industrialisation.

    Par contre je confirme que tous les conteneurs (et notamment weblo) ne permettent pas facilement de définir des elements arbitraires (type string) dans l’arbre JNDI :(

    Une solution de contournement peut s’appuyer sur des system properties pour définir un répertoire racine contenant les fichier properties spécifiques à l’environnement.

  • Bonsoir,

    Désolé pour Weblogic, d’après stackoverflow il y a ce plugin, http://code.google.com/p/weblogic-jndi-startup/ sinon il ne reste plus qu’a se baser sur une variable d’environnement comme le suggère michel. avec weblo j’imagine que vous êtes mutualisés avec d’autres application, il faudra penser à préfixer.

    A ma connaissance c’est possible dans les autres conteneurs classiques (jboss, glassfish, jetty, tomcat et websphere)

  • Bonjour

    Merci pour votre article qui résume bien le sujet.

    Je vois 2 inconvénients à la définition des propriétés dans JNDI:
    – le suivi des changements de config.
    – l’obligation de passer par un serveur JNDI pour modifier des propriétés.

    Sur nos projets nous préférons mettre toutes les propriétés de la webapp dans un fichier « application.properties » qui n’est pas packagé avec la webapp, mais qui est lu au démarrage via le classpath. Pour nos déploiements sur Tomcat il nous suffit de mettre le répertoire conf de Tomcat dans le classpath.

    L’avantage est de pouvoir versionner ce fichier « application.properties » par environnement dans Subversion, et donc de pouvoir suivre qui l’a modifié et quand.

    Ensuite nos scripts de déploiement continu vont automatiquement faire un checkout du fichier « application.properties » au déploiement de l’application.

    Qu’en pensez-vous ?

  • Bonjour

    Dans l’approche que je propose ce sont les chemins vers les fichiers de paramétrage qui sont configurés dans JNDI, pas les paramétrages eux même. J’ai certes configuré la datasource directement dans le conteneur mais on pourrait tout aussi bien avoir le chemin vers un fichier de définition.

    La probabilité de devoir modifier les chemins me parait proche de zéro dans une infra industrialisée.

    Les fichiers eux-même peuvent être gérés et versionnés sans problème. De préférence dans un gestionnaire de configuration ( qui retrouve tout son sens ) peut-être avec différentes branches certaines partagées avec les études d’autres restreintes aux seules équipes d’exploitation.

    Nos deux solutions sont donc équivalentes.

    Sur certains conteneurs, la solution classpath me paraît légèrement moins satisfaisante car il faudra placer les fichiers dans un classloader accessible de toutes les applis web. Ceci dit ça ne m’empêcherait pas de dormir et les chatons du monde entier serait en sécurité.

  • Merci pour votre réponse. Je suis heureux de contribuer au sauvetage mondiale de chatons. :)

  • Bonjour,

    Je rajouterais qu’il peut être intéressant d’avoir un properties par défaut embarqué dans le .war. Dans le placeholder on met ce fichier suivit du fichier de l’environnement (récupéré par jndi).
    Ce fichier je le mets dans le classpath META-INF/application-default.properties …

    Cela permet de simplifier les déploiements tout en offrant un grand nombre de valeurs paramétrables.

  • Bonjour,

    Je pense que la configuration de l’application doit être séparée en deux catégories:
    – les propriétés de l’application, valables où qu’elle s’exécute
    – les propriétés de l’environnement qui ne sont valables que dans un contexte d’exécution précis.
    Avoir des valeurs par défaut pour la première catégorie est acceptable, du moment que les valeurs par défaut sont celles adaptées à la production, afin de réduire au maximum le risque d’exploitation. Avoir un problème de configuration en recette est pénible, en production ça peut vite être critique.
    Avoir des valeurs par défaut pour la seconde catégorie est dangereux : cela peut donner l’impression que le déploiement de l’application s’est bien passé alors qu’elle pointe vers un environnement « par défaut » qui n’a que peu de chances d’être le bon. Le diagnostic des problèmes sera allongé par le fait que l’application semble fonctionner, un éventuel message d’erreur ou des problèmes d’incohérence de données ne seront détectés que très en aval de l’action qui a réellement causé l’erreur et probablement pas une personne qui n’aura pas le contexte nécessaire pour comprendre la source du problème.
    Imaginez que l’application soit déployée en production avec une valeur par défaut qui pointent vers un contexte de test (base de données, service, etc), vos données de production risquent d’être corrompues avant que quelqu’un ne s’en aperçoive. Un tel problème peut être extrêmement compliqué voir impossible à résoudre. Si la valeur manque, l’équipe d’exploitation qui fait le déploiement détectera immédiatement le problème et pourra corriger la configuration ou appliquer la procédure de retour arrière sans que le problème n’ait l’occasion de se propager. Il est préférable d’échouer au plus tôt (« fail fast »), plus un problème est détecté tard, plus son coût de résolution est important.

  • Bonjour,

    Dans le dernier exemple, ne faut-il pas lire dans le constructeur du FileSystemResource :
    jee:jndi-lookup id= »envpropertiepath » jndi-name= »envPropertiePath » ?

  • Bonjour,

    Vous avez tout à fait raison, merci de m’avoir fait remarquer ce problème, je vais le corriger de suite.

  • Hello,

    C’est bien d’avoir fait un rappel des bonnes pratiques de déploiement déjà abordée avec brio par Philippe Prados dans ses papiers:
    http://www.prados.fr/java/javaee/jndi-resources/pourquoi-utiliser-jndi
    http://www.prados.fr/java/javaee/jndi-resources/uniformiser-les-deploiements

    Développeur, développeuses … ne vous laissez pas séduire par la facilité et l’attirance de l’API Properties.

    Un petit rappel du concept de « Paramètre » ne fait pas de mal:
    http://www.prados.fr/java/javaee/les-parametres-en-java

    Bonne lecture.

  • Bonjour,

    Pour tomcat, il est possible d’utiliser la conf suivante:

    <!– Ressources –>
    <bean class= »org.springframework.beans.factory.config.PropertyPlaceholderConfigurer »>
    <!– <property name= »ignoreResourceNotFound » value= »true »/> –>
    <property name= »systemPropertiesModeName » value= »SYSTEM_PROPERTIES_MODE_OVERRIDE » />
    <property name= »locations »>
    <list>
    <value>classpath:app-internal.properties</value>
    <value>file:#{ systemProperties[‘catalina.base’] }/conf/app-external.properties</value>
    </list>
    </property>
    </bean>

    Les avantages:
    Cette configuration charge des propriétés depuis le classpath, puis depuis un fichier externe avec un path base sur la racine de tomcat. Donc pas besoin d’inclure le dossier conf dans le classpath. Par ailleurs, les sont overridables via les propriétés système. Au démarrage Tomcat ajout la propriété système ‘catalina.base’. Il n’y a donc aucune configuration à faire.

    Les inconvénients:
    Dans l’exemple, le mécanisme repose sur une propriété système de Tomcat. Cependant, il est possible de pointer sur le dossier conf externalisé de votre choix.

  • La dernière proposition couple fortement l’application au conteneur, ce qui est dommage dans certains cas (conteneur différent entre postes de dev et autres environnements), migration future du conteneur d’application et risque d’oubli de la subtilité.
    Le chemin via JNDI reste une solution plus souple.

    A noter qu’UrlResource peut aussi être utilisé.

Laisser un commentaire