Configuration dynamique du déploiement d’une application Spring

9

Le mécanisme de découpage des fichiers de configuration Spring permet de répartir les définitions de bean en fonction de leur type. Il est ainsi possible de configurer des éléments, comme la datasource, dans un fichier spécifique. Dans le cadre d’un test on pourra initialiser l’application contexte de Spring avec les fichiers applicationConfig.xml et test-infrastructure.xml, utilisant une dataSpurce locale, et dans un environnement de production on pourra utiliser les fichiers applicationConfig.xm et prod-infrastructure.xml utilisant une dataSource récupérée via JNDI. La question qui se pose maintenant est la suivante : Comment choisir dynamiquement les fichiers à utiliser en fonction de l’environnement de déploiement ?

Prenons l’exemple de code Java suivant :

  1. ApplicationContext context = new ClassPathXmlApplicationContext(
  2. new String[] {
  3. « classpath:com/zenika/spring/config/applicationContext.xml »,
  4. « classpath:com/zenika/spring/config/test-infrastructure.xml »,
  5. });

Pour s’exécuter dans un environnement de production, il faut modifier ce code afin de remplacer le fichier test-infrastructure.xml par le fichier prod-infrastructure.xml.

Le contenu du fichier test-infrastructure.xml

  1. <beans>
  2. <bean id=« dataSource » class=« org.springframework.jdbc.datasource.DriverManagerDataSource »>
  3. <property name=« driverClassName » value=« org.hsqldb.jdbcDriver »/>
  4. <property name=« url » value=« jdbc:hsqldb:xdb »/>
  5. <property name=« username » value=« sa »/>
  6. <property name=« password » value=«  »/>
  7. </bean>
  8. </beans>

Le contenu du fichier prod-infrastructure.xml

  1. <beans>
  2. <jee:jndi-lookup id=“dataSource” jndi-name=“java:comp/env/jdbc/credit” />
  3. </beans>

Déplaçons maintenant la dépendance à se fichier d’infrastructure au niveau de la configuration XML afin de rendre le code java immuable quel que soit l’environnement de déploiement ou d’exécution.

Le code Java n’importe plus qu’un seul fichier de configuration et peut donc s’écrire :

  1. ApplicationContext context = new ClassPathXmlApplicationContext(« classpath:com/zenika/spring/config/applicationContext.xml »);

Modifions maintenant le fichier applicationContext.xml afin d’utiliser la balise <import/> et d’importer le fichier qui nous intéresse avec la définition de la dataSource

  1. <beans>
  2. <bean id=« demoRepository » class=« com.zenika.spring.config.DemoRepository »>
  3. <constructor-arg ref=« dataSource »/>
  4. </bean>
  5. <import resource=« test-infrastructure.xml »/>
  6. </beans>

Dernière étape, rendre dynamique le choix du fichier importé en fonction d’une propriété système. On modifie pour cela la commande d’import en déclarant ici une variable ${env}

  1. <beans>
  2. <bean id=« demoRepository » class=« com.zenika.spring.config.DemoRepository »>
  3. <constructor-arg ref=« dataSource »/>
  4. </bean>
  5. <import resource=« ${env}-infrastructure.xml »/>
  6. </beans>

Il ne reste plus qu’à renseigner cette variable au démarrage de l’application, ou de la classe de test Java, en settant une propriété système.

  • -Denv=prod pour utiliser le fichier prod-infrastructure.xml
  • -Denv=test pour utiliser le fichier test-infrastructure.xml

Le remplacement de la variable ce fait par Spring au niveau de la classe DefaultBeanDefinitionDocumentReader

  1. /**
  2.  * Parse an « import » element and load the bean definitions
  3.  * from the given resource into the bean factory.
  4.  */
  5. protected void importBeanDefinitionResource(Element ele) {
  6. String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
  7. if (!StringUtils.hasText(location)) {
  8. getRea derContext().error(« Resource location must not be empty », ele);
  9. return;
  10. }
  11. // Resolve system properties: e.g. « ${user.dir} »
  12. location = SystemPropertyUtils.resolvePlaceholders(location);
  13. }
Partagez cet article.

A propos de l'auteur

9 commentaires

  1. Merci beaucoup pour l’astuce de l’utilisation de properties passées à la JVM pour controler l’inclusion des fichiers.

    Je me suis cassé les dents maintes fois dessus en oubliant de changer le fichier properties à inclure avant d’exporter et livrer au client :)

  2. Bonjour, merci pour ce tutoriel !!!

    J’ai un soucis quand j’utilise votre méthode, j’ai le droit à un beau message d’erreur : Marshalling Error : ${env}

    Je précise que je passe plusieurs paramètre à mon programme …

    Merci !

  3. J’ai beau tout tenter je n’y arrive pas.

    Voici mon fichier applicationContext.xml

    <?xml version= »1.0″ encoding= »UTF-8″?>
    <beans xmlns= »http://www.springframework.org/sche…« 

    http://www.springframework.org/schehttp://www.springframework.org/sche
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws…« >

    </beans>

    Et mon programme de test

    public class Test {
    public static void main(String args) {
    try{
    System.out.println(« ici »);
    ApplicationContext context = new ClassPathXmlApplicationContext(« classpath:applicationContext.xml »);
    ClientGeide clientGeide = (ClientGeide)context.getBean(« clientGeide »);
    boolean retour = clientGeide.archiveMSGFromOutlook(« test »+ »-« +System.currentTimeMillis(), « bla », « leader », « 10 », «  », «  »);
    System.out.println(retour);
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }

    J’ai le droit à une belle erreur :

    INFO: Creating Service {http://unknown.namespace/}ClientGei… from class ClientGeide
    …..
    Caused by: java.net.UnknownHostException: ${env}

    J’ai même tenté de passer le paramètre avec la ligne de commande System.setProperty(« env », « localhost »); mais cela n’a rien changé

    HELP SVP

  4. Bonjour,

    C’est normal que cela ne fonctionne pas dans le cas présent. La fonctionnalité mise en avant dans ce billet concerne uniquement les imports de fichiers de configuration Spring. En fait, dans ce mécanisme, il a été prévu au niveau du code Java de pouvoir utiliser ce type de variables.

    Ceci est illustré par le dernier exemple de code du billet, reprenant la partie concernée au niveau de la méthode importBeanDefinitionResource de la classe DefaultBeanDefinitionDocumentReader. Dans la JavaDoc de ce même code du billet, on peut lire la chose suivante :
    ** Parse an « import » element and load the bean definitions **

    Malheureusement dans le cas que tu utilises, Spring ne saura interpréter cette variable.

    En espérant avoir répondu à ta question.

  5. En fait pour ton problème, la solution la plus simple consisterais à mettre ton bean <jaxws: /> dans un autre fichier xml et l’importer dans le fichier de configuration principale…

    Tu pourrais ainsi avoir dans ton fichier de configuration principale et utiliser à ce moment le ${env}

  6. Ou alors, tu peux créer un fichier de configuration XML par environnement, au sein duquel tu déclares un PropertyPlaceholderConfigurer pointant sur un fichier .properties propre à cet environnement.
    Il ne reste plus ensuite qu’à importer le bon fichier de configuration à l’aide de la technique décrite dans l’article.

    Bien sûr, ton bean <jaxws:client> utilisera une variable placeholder définie dans les fichiers .properties.
    <jaxws:client (…) address= »${gedServiceUrl} » />

  7. Bonjour ! Pour info., si votre application web a besoin de manipuler des données provenant d’une base de données, et que vous devez vous y connecter depuis le contexte JNDI de votre serveur d’applications, il existe plusieurs chemins qui permettent d’y arriver. Je présente deux solutions dans mon article ici http://bit.ly/buNcNs

Ajouter un commentaire