| Ce site met a disposition le build journalier de la traduction francaise du Maven: The Definitive Guide Consultez : | ![]() |
Si vous jetez un oeil aux différents POMs créés dans le Chapitre 7, Un projet multimodule d'entreprise, vous remarquerez plusieurs types de
répétition. Le premier type que vous pouvez voir est la duplication de dépendances comme spring
et hibernate-annotations dans plusieurs modules. La dépendance hibernate a en
outre l'exclusion de javax.transaction à chacune de ses définitions. Le second type de répétition rencontré vient du fait que parfois plusieurs dépendances sont liées entre
elles et partagent la même version. C'est souvent le cas lorsqu'un projet livre plusieurs composants couplés entre
eux. Par exemple, regardez les dépendances hibernate-annotations et
hibernate-commons-annotations. Toutes les deux ont la même version 3.3.0.ga,
et nous pouvons nous attendre à ce que les versions de ces deux dépendances évoluent de concert. Les deux artefacts
hibernate-annotations et hibernate-commons-annotations sont des composants du
même projet mis à disposition par JBoss, et donc quand une nouvelle version de ce projet sort, ces deux dépendances
changent. Le troisième et dernier type de répétition est la duplication de dépendances et de version de modules frères. Maven fournit un mécanisme simple pour
vous permettre de factoriser ces duplications dans un POM parent.
Comme pour le code source de votre projet, chaque duplication dans vos POMs, est une porte ouverte pour de futurs problèmes . La cohérence des versions sur un gros projet sera difficile à assurer si des déclarations de dépendance sont dupliquées. Tant que vous avez deux ou trois modules, cela reste gérable, mais lorsque votre organisation utilise un énorme build multimodule pour gérer des centaines de composants produits par plusieurs services, une seule erreur de dépendance peut entraîner chaos et confusion. Une simple erreur de version sur une dépendance permettant la manipulation de bytecode comme ASM dans les profondeurs des dépendances du projet peut devenir le grains de sable qui gripperait une application web, maintenue par une autre équipe de développeurs, et qui dépendrait de ce module. Les tests unitaires pourraient passer car ils sont exécutés avec une certaine version de la dépendance, mais ils échoueraient lamentablement en production là où le package (un WAR, dans ce cas) serait réalisé avec une version différente. Si vous avez des dizaines de projets qui utilisent une même dépendance comme Hibernate Annotations, chaque recopie et duplication des dépendances et des exclusions vous rapproche du moment où un build échouera. Au fur et à mesure que la complexité de vos projets Maven augmente, la liste de vos dépendances s'allonge et vous allez donc devoir stabiliser les déclarations de dépendance et de version dans des POMs parent.
La duplication des versions des modules frères peut produire un problème assez redoutable qui ne résulte pas directement de Maven, et dont on ne se souvient qu'après l'avoir rencontré
plusieurs fois. Si vous utilisez le plugin Maven Release pour effectuer vos livraisons, toutes les versions de
dépendance soeurs seront automatiquement mises à jour pour vous, aussi ce n'est pas là que réside le problème. Si
simple-web version 1.3-SNAPSHOT dépend de simple-persist
version 1.3-SNAPSHOT, et si vous produisez la version 1.3 de ces deux projets, le plugin Maven
Release est suffisamment intelligent pour changer les versions dans les POMs de votre projet
multimodule automatiquement. Produire la livraison avec le plugin Release va automatiquement incrémenter les
versions de votre build à 1.4-SNAPSHOT, et le plugin Release va commiter ces modifications sur le
dépôt de source. Livrer un énorme projet multimodule ne pourrait être plus facile, à moins que...
Les problèmes arrivent lorsque les développeurs fusionnent les modifications du POM et perturbent une livraison en cours. Un développeur fusionne souvent des
modifications et parfois il se trompe lors de la gestion du conflit sur la dépendance sur un module frère, revenant
par inadvertance à la version de la livraison précédente. Comme les versions consécutives d'une dépendance sont
souvent compatibles, cela n'apparaît pas lorsque le développeur lance le build, ni avec un système d'intégration
continue. Imaginez un build très complexe où le tronc est rempli de composants à la version
1.4-SNAPSHOT, et maintenant imaginez que le Développeur A a mis à jour le Composant A tout au
fond de la hiérarchie du projet pour qu'il dépende de la version 1.3-SNAPSHOT du Composant B.
Même si la plupart des développeurs utilisent la version 1.4-SNAPSHOT, le build fonctionne
correctement si les versions 1.3-SNAPSHOT et 1.4-SNAPSHOT du Composant B sont
compatibles. Maven continuera à construire le projet en utilisant la version 1.3-SNAPSHOT du
Composant B depuis le dépôt local des développeurs. Tout semble bien se passer — le projet est construit,
l'intégration continue est au vert, etc. Quelqu'un peut alors rencontrer un bug étrange en rapport avec le Composant
B, mais il va se dire que c'est la faute à "pas de chance" et va poursuivre son développement. Pendant ce temps,
dans la salle des machines la pression monte, jusqu'à ce qu'une des pièces explose ...
Quelqu'un, appelons le Mr. Distrait, a rencontré un conflit lors de la fusion du Composant A, et a
malencontreusement indiqué que le Composant A dépend de la version 1.3-SNAPSHOT du Composant B
alors que le projet continuait sa marche en avant. Une équipe de développeurs essaye de corriger un bug dans le
Composant B depuis tout ce temps et ils ne comprennent pas pourquoi ils n'arrivent pas à le corriger en production.
Finalement, quelqu'un regarde le Composant A et s'aperçoit de cette dépendance sur une mauvaise version.
Heureusement, ce bug n'était pas suffisamment important pour coûter de l'argent ou des vies, mais Monsieur Distrait
se sent un peu bête et les autres ont perdu un peu de leur confiance en lui pour ce problème de dépendances entre
composants. (Heureusement, Monsieur Distrait se rend compte que ce problème est une erreur d'utilisateur et ne vient
pas de Maven, mais il est plus que probable qu'il commence un vilain blog dans lequel il se plaint de Maven sans
arrêt pour se sentir mieux.)
Heureusement, la duplication de dépendance et les erreurs de dépendance entre projets frères peuvent être
facilement évitées avec quelques modifications. La première chose à faire est de trouver toutes les dépendances
utilisées sur plus d'un projet et de les déplacer dans la section dependencyManagement du
POM parent. Nous allons laisser de côté les dépendances entre modules frères pour l'instant. Le
pom simple-parent contient maintenant :
<project>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
...
</project>
Une fois ces dépendances remontées, nous allons devoir supprimer les versions de ces dépendances de tous les
POMs ; sinon, elles vont surcharger ce qui est défini dans la balise
dependencyManagement du projet parent. Regardons juste le simple-model pour
plus de clarté :
<project>
...
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
</dependency>
</dependencies>
...
</project>
La seconde chose à faire est de corriger la duplication des versions de
hibernate-annotations et hibernate-commons-annotations puisqu'elles devraient
rester identiques. Nous allons faire cela en créant une propriété appelée
hibernate.annotations.version. La section simple-parent résultante ressemble à
ceci :
<project>
...
<properties>
<hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version>
</properties>
<dependencyManagement>
...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>${hibernate.annotations.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>${hibernate.annotations.version}</version>
</dependency>
...
</dependencyManagement>
...
</project
Notre dernier problème à corriger est celui des dépendances entre modules frères. Une technique que nous pourrions utiliser est de les déplacer dans la section
dependencyManagement, comme toutes les autres, et de définir les versions des projets frères dans le projet parent qui les contient. Cette approche
est certainement valide, mais nous pouvons aussi résoudre ce problème de version en utilisant deux propriétés
prédéfinies — ${project.groupId} et ${project.version}. Puisqu'il s'agit de
dépendances entre frères, il n'y a pas grand-chose à gagner à les lister dans le parent, aussi nous allons faire
confiance à la propriété prédéfinie ${project.version}. Comme ces projets font tous partie du
même groupe, nous pouvons sécuriser d'avantage ces déclarations en faisant référence au groupe du
POM courant via la propriété prédéfinie ${project.groupId}. La section
dependency de simple-command ressemble maintenant à cela
:
<project>
...
<dependencies>
...
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>simple-weather</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>simple-persist</artifactId>
<version>${project.version}</version>
</dependency>
...
</dependencies>
...
</project>
Voici un résumé des deux optimisations que nous avons réalisées pour réduire ce problème de duplication des dépendances :
dependencyManagementSi plus d'un projet dépend d'une dépendance particulière, vous pouvez ajouter celle-ci à
dependencyManagement. Le POM parent peut contenir une version et un
ensemble d'exclusions ; tout ce que les POMs fils ont besoin de faire pour référencer cette
dépendance est d'utiliser le groupId et l'artifactId. Les projets fils
peuvent ne pas déclarer la version et les exclusions si la dépendance fait partie des dépendances de la balise
dependencyManagement.
version et le groupId pré-définis pour les
projets frèresUtiliser ${project.version} et ${project.groupId} pour faire
référence à un projet frère. Les projets frères ont la plupart du temps le même groupId, et
presque toujours la même version. Utiliser ${project.version} vous aidera à éviter les incohérences
de versions entre projets frères comme nous l'avons vu plus tôt.