Spring 3.1 - Profils et environments (partie 1)


Le framework Spring, que l'on ne présente plus, est en évolution constante. Chaque version ajoute son lot de nouveauté pour augmenter autant la flexibilité que la facilité d'utilisation des outils. La prochaine version majeure en date est la version 3.1 annoncée pour le mois de Juin 2011 (si tout va bien !).
Bien qu'encore en développement, une première version du framework (milestone 1) est déjà disponible et permet de prévisualiser certaines fonctionnalités d'ores et déjà implémentées.
Cette suite de billets à pour but de présenter ces nouveautés annoncées/disponibles.

Le premier billet sera une introduction au concept de Profils et Environnements, présents dans la première milestone.

Exemples de code

Pour illustrer chaque fonctionnalité, un projet est disponible sur GitHub permettant de retracer l'évolution d'une application et de voir quelles sont les modifications apportées. Chaque point où le code sera mis à jour, un tag ou un identifiant Git permettront d'obtenir le projet en état sur GitHub.
Attention, ce projet servant uniquement de démonstration, il pourrait potentiellement voir son historique réécrit pour plus de clarté (l'article serait mis à jour évidemment).

Pour récupérer le code, il suffit de faire :

git clone git://github.com/ColinHebert/Spring-3.1-Demo.git
git checkout <branch_name>

DEMO : Une première version de l'application (qui n'utilise pas Spring) est disponible au tag springless.

Spring 3.1, Quoi de neuf ?

Milestone La version majeure actuelle de Spring Framework (3.0) est disponible depuis le mois de décembre 2009.
Cette version a apporté entre autres une forte compatibilité avec les nouveautés accompagnant JEE6, telles que Bean Validation (JSR 303), Dependency Injection for Java (JSR-330), JPA 2.0 (JSR 317 et bien d'autres, dans un même temps un nouveau système de configuration d'application Spring en java a été mis en avant. Basé sur les classes, ce système permet de créer des classes décrivant la configuration souhaitée, grâce à l'annotation @Configuration.

Dans la continuité de cette amélioration du framework, la version 3.1 propose une plus forte interaction avec d'autres frameworks et outils tout en continuant à améliorer l'utilisabilité par les développeurs.

Ces features ont étés étalées sur deux version milestone de Spring 3.1, l'une sortie au mois de février proposant :

  • La gestion des profils et environnements.
  • L'amélioration de la configuration Spring en java (@FeatureConfiguration).
  • L'ajout d'un namespace supplémentaire c: pour aider et simplifier la configuration XML.
  • Une abstraction des systèmes de cache.

La seconde milestone, initialement prévue pour le mois d'avril, devrait intégrer :

  • Une personnalisation de la gestion de l'annotation @MVC
  • La gestion des conversations
  • Le support des Servlets 3.0
  • L'amélioration du support de Groovy

Attention, dû à la nature "work in progress" de Spring 3.1, certaines de ces fonctionnalités pourraient de pas figurer sur la version finale et être reportées à des versions ultérieures.

Profils et Environnements

Environment Parmi les fonctionnalités attendues et déjà disponibles dans la première milestone, figure la gestion des profils et environnements.

Pourquoi ?

L'idée, relativement simple, est de donner la possibilité de créer une configuration spring s'adaptant automatiquement à l'environnement dans lequel l'application est exécutée.

L'une des utilisations clefs est le changement et la définition de l'environnement d'execution. Par exemple, si l'on a une application qui utilise de la persistance, il est fort probable que durant les différentes phases de développement, la persistance se fasse au travers de systèmes différents :

  • Durant le développement en soit, les tests unitaires sont exécutés avec une base de données embarquée.
  • Pour certains des tests unitaires une véritable base de données n'est peut-être même pas nécéssaire (par exemple si l'on veut tester la couche service sans que la couche DAO puisse interférer avec les résultats) et c'est une Map/List qui sera utilisée pour un stockage sous la forme la plus simple possible.
  • Durant l'intégration et le déploiement, la connection à la base est obtenue via une DataSource récupérée par JNDI.

Tant de configurations possibles qui demandent chaque fois un nouveau fichier de configuration. Chaque configuration est totalement adaptée aux conditions d'execution, cependant la plupart les configurations ont de fortes similarités, ce qui donne des configurations copiées/collées/modifiées, pour chaque variante que l'on a.
Une autre solution est de composer une configuration à base d'import d'autre fichiers de configuration. Outre le fait que ce système implique d'avoir un florilège de fichiers XML éparpillant la configuration, il faut être extrèmenent prudent lorsque l'on fait les imports pour éviter qu'un import transitif soit fait et rentre en conflict avec d'autres éléments déjà déclarés.
Et généralement pour retourner le couteau dans la plaie, le fait d'avoir plusieurs fichiers de configuration signifie que l'on ne peut pas (sauf si l'on a appliqué une nomenclature de bonne qualité) charger simplement tous les fichiers de configuration avec un masque et que l'on doit charger manuellement tous les fichiers un par un.

DEMO : Le contact book configuré avec un fichier XML de Spring est disponible sur le tag spring-xml.
L'application étant plutôt petite, la configuration est relativement légère et ne subit pas fortement les problèmes cités au dessus, cependant une application de plus grande envergure pourrait plus facilement y être exposée.

Comment ?

Le principe choisi est d'assigner aux différentes portions de configuration un profil (une simple chaine de caractères). Si le profil est activé au chargement de la configuration, alors la portion de configuration associée est effective, dans le cas contraire, elle est purement ignorée.
Si les profils permettent de modulariser la configuration leur état (actif/inactif) est lui conservé au sein de l'environnement qui permet donc de déduire quelles portions de configuration sont utilisées.

Charger des fichiers de configuration en bloc

La première étape est de proposer une configuration modulaire.
Pour ça la dernière version du XSD de configuration spring a été remaniée de manière à ajouter un attribut profile sur la balise racine.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation=""http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
       profile="myProfile">
    ...
</beans>

Spécifier le profil "myProfile" dans la configuration permet, lorsque le fichier de configuration est chargé, de ne prendre en compte le contenu que si le profil "myProfile" est activé dans l'environnement actuel.
Si aucun profil n'est spécifié, le fonctionnement habituel est appliqué; le fichier est simplement traité.

Ceci implique donc qu'il est possible de charger en même temps deux fichiers de configuration en conflit si ceux-ci ont des profils différents.
Le problème de chargement de fichiers de configuration manuel est donc réduit, puisqu'il est potentiellement possible de charger tous les fichiers de configuration Spring, tant que les conflits sont évités dans un même profil.

Fusionner les fichiers de configuration

Toujours dans l'idée de proposer de faire des modules au sein d'une configuration, une autre modification a été faite sur le XSD de configuration. Il est maintenant possible d'ajouter une nouvelle balise <beans> (avec un 's') en fin d'un fichier de configuration.
Ces balises peuvent contenir une configuration complète (comme la balise racine) et avoir un profil particulier.

<beans ...>
    <bean ... />
    <beans profile="myProfile">
        <bean ... />
    </beans>
</beans>

Le fonctionnement est exactement le même qu'un profil sur la racine; le contenu n'est pris en compte que si le profil "myProfile" est activé.
Il est maintenant possible d'ajouter des portions de configuration, basées sur l'activation de profils, au sein d'un même fichier de configuration.
Grâce à ce système, le nombre de "micro-fichiers" inclus pour une configuration peut être fortement réduit.

DRY (Don't Repeat Yourself)

Le dernier point important est d'éviter d'avoir à copier des morceaux de configuration pour deux profils ayant des similarités. Pour ça, l'attribut profile permet de déclarer plusieurs profils pour un même "groupe".

<beans ... profile="myProfile, myOtherProfile">
     ...
</beans>

De cette manière, si l'un des deux profils ("myProfile" ou "myOtherProfile") est activé, alors le contenu est appliqué dans la configuration Spring.
Le résultat est donc un fichier plus compact avec moins de répétition et de fait beaucoup plus facilement maintenable.

Activer les profils

Maintenant que la configuration est définie par profils, il suffit de spécifier s'ils sont activés ou non.
Il est possible d'activer plusieurs profils simultanément, attention cependant de ne pas activer deux profils incompatibles !

Option en ligne de commande

Le moyen le plus classique de choisir le ou les profils actifs est de lancer l'application avec l'option -Dspring.profiles.active="myProfile".
Si plusieurs profils sont à activer simultanément, il suffit de tous les lister en les séparant par une virgule : -Dspring.profiles.active="myProfile,myOtherProfile".

Dans une application web

Dans le cas d'une application web il est possible de spécifier dans le web.xml la liste des profils en paramètre de la servlet :

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>myProfile</param-value>
    </init-param>
</servlet>
Manuellement au chargement de la configuration

Il est aussi possible de configurer manuellement les profils au chargement du fichier de configuration.
Pour cela il faut récupérer l'environnement à partir du contexte Spring; et de là assigner les profils voulus :

// context.getEnvironment().setActiveProfiles("myProfile", "myOtherProfile"); //

DEMO : Voici le contact book ayant été remanié pour ajouter la gestion des profils : profile-xml.
Cette démonstration ne montre pas un cas "réel" en soit, puisque par exemple il aurait été préférable d'avoir tout de même une séparation entre les fichiers de configuration de test et de production. Seulement pour conserver une application de taille raisonnable et une visualisation simple quelques petites entorses ont été faites.
La sélection des profils est faite manuellement pour éviter d'avoir des résultats différents si ceux-ci n'ont pas été sélectionnés, il est évidemment possible de supprimer la selection manuelle pour utiliser la propriété spring.profiles.active.

Conclusion

Ceci conclu la première partie sur les profils et environnements dans Spring 3.1, un second article détaillera le fonctionnement des profils dans une configuration Spring en Java (@Configuration).

Petit message d'avertissement tout de même, cette fonctionnalité, qui est très pratique a n'en pas douter, ne doit pas être abusée. S'il est plus facile d'obtenir une configuration plus succinte grâce aux profils, un seul fichier de configuration monolithique pour une application de taille pourrait rendre le tout inmaintenable.
De la même manière, il est toujours bon de garder séparé les profils de test des profils disponibles en production, inutile d'encombrer l'application distribuée d'un ensemble de profils qui ne seront globalement pas utilisés.


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.