Configuration dynamique du déploiement d'une application Spring


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.  
  6. <import resource="test-infrastructure.xml"/>
  7. </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.  
  6. <import resource="${env}-infrastructure.xml"/>
  7. </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. getReaderContext().error("Resource location must not be empty", ele);
  9. return;
  10. }
  11.  
  12. // Resolve system properties: e.g. "${user.dir}"
  13. location = SystemPropertyUtils.resolvePlaceholders(location);
  14.  
  15. ...
  16. }

Commentaires

1. Le jeudi 2 juillet 2009, 02:52 par Jawher

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. Le mercredi 5 août 2009, 15:49 par kilicool

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. Le mercredi 5 août 2009, 16:44 par kilicool

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..."

      xmlns:jaxws="[http://cxf.apache.org/jaxws]"
      xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance]"
      xsi:schemaLocation="

http://www.springframework.org/sche... http://www.springframework.org/sche...
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws...">

   <jaxws:client id="clientGeide"
                 serviceClass="ClientGeide"
                 address="[http://${env}/novanet/services/GEDService]" />

</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. Le mercredi 5 août 2009, 19:19 par Carl Azoury

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. Le vendredi 7 août 2009, 10:37 par kilicool

Oui merci bcp !

As tu une idée de comment géré mon problème de variable ?

Merci à toi

6. Le lundi 10 août 2009, 09:52 par kilicool

Non ?

7. Le lundi 10 août 2009, 10:38 par Carl Azoury

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}

8. Le lundi 10 août 2009, 10:38 par Olivier Croisier

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}" />

9. Le mardi 26 janvier 2010, 21:00 par Des Geeks et des lettres

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

Fil des commentaires de ce billet

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.