Publié par

Il y a 8 années -

Temps de lecture 5 minutes

Grails Tips : externaliser la configuration

Grails, c’est bien. Ça permet des développements rapides, simples et efficaces. Mais à moins de faire du jetable pour tester, un jour, il faut livrer. Se pose alors, souvent tard, la question de la configuration de l’application au runtime.

Nous allons voir ensemble quelques solutions pour externaliser proprement les clés de configuration pour un projet basé sur Grails.

Out of the box

Grails, par défaut, nous offre la possibilité de charger des fichiers de configuration externe de façon assez simple. Le chargement s’effectue suivant une logique simple :

  • Tout ce qui se trouve dans le fichier Config.groovy vient en premier
  • Toutes les clés présentes dans les fichiers externes viennent s’ajouter à la configuration, ou surcharger les valeurs de Config.groovy.

Ce mécanisme est documenté et très simple à mettre en œuvre. Il suffit de placer les quelques lignes suivantes au début du fichier grails-app/conf/Config.groovy :

grails.config.locations = [ "classpath:${appName}-config.properties",
                            "classpath:${appName}-config.groovy",
                            "file:${userHome}/.grails/${appName}-config.properties",
                            "file:${userHome}/.grails/${appName}-config.groovy" ]

Comme on peut le voir dans cet exemple tiré de la documentation, on peut même utiliser des variables dans les chemins spécifiés. Plutôt simple, n’est-il pas ? Le problème est réglé ? Ça conviendra très bien à la plupart des petits projets montés en local, cependant, l’utilisation de chemins en dur (ou modulo certaines variables) directement dans le Config.groovy est un peu rigide. Il est souvent problématique de livrer un war à un exploitant en lui expliquant que le fichier de configuration doit se placer à tel chemin et pas un autre.

Voyons si on peut trouver mieux…

Par variable d’environnement ou propriété JVM

L’idéal serait de pouvoir définir le chemin vers le fichier de configuration depuis l’extérieur du war. Pour faire cela, on peut se baser sur des variables d’environnement. Le fichier Config.groovy est, avant toute chose, un script Groovy ! Par conséquent, aucun problème pour inclure un peu de code dedans et récupérer des valeurs depuis le système.

On pourrait par exemple remplacer l’exemple du précédent paragraphe par :

// On fixe le nom de la variable d'environnement qui nous intéresse
def ENV_NAME = "APPNAME_CONFIG"

// Si la liste grails.config.location n'existe pas, on l'instancie
if(!grails.config.locations || !(grails.config.locations instanceof List)) {
    grails.config.locations = []
}

// Si la variable est présente dans l'environnement ...
if(System.getenv(ENV_NAME)) {

  // ... on inclut sa valeur dans les configuration à charger
  println "Including configuration file specified in environment: " + System.getenv(ENV_NAME);
  grails.config.locations << "file:" + System.getenv(ENV_NAME)
} else if (System.getProperty(ENV_NAME)) {

  // Sinon on regarde dans les propriétés JVM et on l'inclut si elle est présente
  println "Including configuration file specified on command line: " + System.getProperty(ENV_NAME);
  grails.config.locations << "file:" + System.getProperty(ENV_NAME)
} else {

  // Si vraiment on ne trouve rien, on le dit quand même, parce que ça se fait.
  println "No external configuration file defined."
}

Extrait de cet article et commenté maison

Si vos administrateurs sont complaisants, cette méthode peut être votre solution. Mais beaucoup rechigneront à toucher à leur environnement système pour faire plaisir à votre code chéri (du moins c’était le cas parmi ceux que j’ai rencontrés). Cela risque également de poser des problèmes dans le cas où l’on souhaite faire tourner plusieurs instances de notre application sur le même serveur avec des configurations différentes. Dans ce cas, la grimace de l’administrateur devant son environnement système se remplissant de toute une famille de variables vaut le coup d’œil.

Poussons donc un peu plus loin…

Par entrée JNDI

Plutôt que de toucher à l’environnement du système, nous pouvons nous contenter de toucher à l’environnement d’exécution de notre application. JNDI arrive à la rescousse. En s’appuyant non pas sur des variables système mais sur des variables JNDI, on cantonne nos modifications au serveur d’applications. Le lien entre l’instance et son fichier de configuration est plus étroit et donc plus évident à manipuler.

Modifions une dernière fois notre en-tête de chargement de configuration (toujours dans Config.groovy) :

import javax.naming.InitialContext

// Si la liste grails.config.location n'existe pas, on l'instancie
if(!grails.config.locations || !(grails.config.locations instanceof List)) {
    grails.config.locations = []
}

try {
  // On tente de récupérer la variable d'environnement JNDI
  String extConfFile = (String) new InitialContext().lookup("java:comp/env/myAppInstanceConfig")

  // Si on trouve notre valeur, on l'ajoute aux fichiers à charger
  if (extConfFile) {
    grails.config.locations << extConfFile
    println "External configuration file loaded : ${extConfFile}"
  }
} catch (Exception e) {
  // En cas d'absence de la variable, on log
  println "Unable to load external configuration, using default."
}

Il suffira pour appliquer une configuration particulière, de définir la variable JNDI pour notre déploiement. Si on prend un serveur d’applications au hasard, Tomcat par exemple (oui, le hasard fait bien les choses), cela donnerait un fichier de contexte ressemblant à ceci:

<Context path="/myAppInstance1" debug="0" docBase="/the/full/path/to/myApp.war">

  <Environment name="myAppInstanceConfig" type="java.lang.String" override="false"
               value="file:/the/full/path/to/myApp-instance1-config.properties"/>

</Context>

Conclusions

Même si toutes ces méthodes sont valides, j’avoue ma préférence pour la dernière. Elle présente à mon sens de multiples avantages:

  • pas d’impact sur le système
  • possibilité de multiplier les instances avec des configurations différentes
  • lien fort entre l’instance d’application et le chemin vers son fichier de configuration

Ce qu’il faut retenir de ces propositions, c’est surtout que Config.groovy est avant tout un script. Il est donc très facile d’ajouter sa propre logique de récupération des fichiers de configuration à charger. On pourrait en imaginer d’autres, comme d’aller parser un fichier centralisé sur le système ou de se connecter à une base de données pour récupérer des clés de configuration.

Le système de configuration incrémentale sur lequel est basé Grails et la configuration par scripting, permettent une très grande flexibilité, ou comme dirait les Perlistes: Tim Toady!.

Ressources

Publié par

Publié par Aurélien Maury

Aurélien est passionné par les frameworks web haute productivité comme Grails, JRuby on Rails ou Play! framework. Il est également intéressé par tous les aspects de performance et d'optimisation (mais aussi par la phytothérapie, la PNL, la basse électrique, la philosophie et pleins d'autres sujets). Il est également formateur au sein de Xebia Training .

Commentaire

4 réponses pour " Grails Tips : externaliser la configuration "

  1. Publié par , Il y a 8 années

    Excellent article, I would have add a small part on JNDI data source, even if it straightforward ;0

  2. Publié par , Il y a 8 années

    Yes, it is actually straightforward, JNDI datasource are supported out of the box by Grails. But let’s write it down. In your project you can set your grails-app/conf/DataSource.groovy like this :

    environments {
        development {
            dataSource { ... }
        }
        test {
            dataSource { ... }
        }
        production {
            dataSource { 
               jndiName = "java:comp/env/myDataSource"
            }
        }
    }
    

    And create a « myDataSource » resource in the Tomcat context of your application. (look here for more on Resources : http://goo.gl/ps9ot)

  3. Publié par , Il y a 6 années

    Merci pour cet article. Mais chez moi ça ne marche pas. Quand je fait un « grails war », tout s’arrête quand on atteint la ligne String extConfFile = (String) new InitialContext().lookup(« java:comp/env/myAppInstanceConfig ») avec une erreur javax.naming.NoInitialContextException

    Du coup je n’arrive même pas à créer le war pour le tester sur tomcat

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.