Comment un projet Maven AEM construit réellement un package déployable
Comment le pom.xml parent et les modules core, ui.apps, ui.content, ui.config et all d'un projet AEM se combinent pour produire un unique package déployable, et pourquoi le module all est la véritable unité de déploiement.
Un projet AEM généré avec l’AEM Project Archetype ne produit pas un seul
artefact : il en produit une demi-douzaine. La confusion habituelle pour
quelqu’un qui découvre un projet AEM n’est pas “que fait Maven”, mais “lequel
de tous ces pom.xml est réellement déployé sur l’instance”. La réponse
courte : presque aucun individuellement — ce qui est déployé est le résultat
d’un module précis, all, qui assemble tous les autres.
La structure multimodule générée par l’archetype
En générant un projet avec com.adobe.aem:aem-project-archetype, on obtient
un pom.xml parent (agrégateur) à la racine et plusieurs modules enfants,
chacun avec son propre pom.xml et son propre type d’empaquetage. Les
modules standards sont :
core— un bundle OSGi (jar) contenant services, listeners, schedulers, Sling Models et servlets : tout le code Java du projet.ui.apps— un content-package déployé dans/apps: HTL, client libraries et définitions de composants.ui.content— un content-package avec du contenu mutable d’exemple (pages, configuration du site sous/contentet/conf).ui.config— un content-package avec des configurations OSGi spécifiques par runmode et des scripts Repo-init.ui.frontend— un build frontend (npm/webpack) dont le résultat (JS/CSS compilés) finit empaqueté dansui.apps.all— un content-package “conteneur” sans contenu propre : il ne fait qu’embarquer les artefacts des modules précédents.
Les détails internes de ui.apps et ui.frontend méritent leur propre
article — ce qui compte ici, c’est la façon dont tous les modules s’assemblent
pour produire le package final, pas leur fonctionnement interne respectif.
Deux types d’empaquetage : bundle vs. content-package
core est le seul module avec un empaquetage jar classique : c’est un
bundle OSGi comme un autre, compilé et empaqueté avec le plugin de bundle
habituel. Les autres modules producteurs de contenu (ui.apps, ui.content,
ui.config, all) utilisent l’empaquetage content-package, construit par
le filevault-package-maven-plugin (org.apache.jackrabbit). Ce plugin
a remplacé l’ancien content-package-maven-plugin d’Adobe/Day et est
actuellement le seul supporté sur AEM as a Cloud Service.
Chaque module de type content-package déclare également un packageType
dans la configuration du plugin :
application→ui.apps(code immuable, déployé dans/apps).content→ui.contentetui.config(contenu et configuration mutables).container→all(n’embarque que d’autres packages, sans contenu ni code propre).
Cette classification n’est pas cosmétique : la validation de packages de
Cloud Manager s’en sert pour rejeter les builds où, par exemple, un package
marqué application touche des chemins /content qui ne lui appartiennent
pas.
Le module all : comment le package final est assemblé
Le pom.xml de all déclare des dépendances de type zip (ou jar pour
core) vers les autres modules, et la configuration du
filevault-package-maven-plugin utilise <embeddeds> pour indiquer à quel
chemin d’installation chacun doit être embarqué dans le package conteneur.
C’est l’approche actuelle ; l’ancien mécanisme <subPackages> est déprécié.
<!-- all/pom.xml (fragment) -->
<plugin>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>filevault-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<group>com.myproject</group>
<packageType>container</packageType>
<embeddeds>
<embedded>
<groupId>com.myproject</groupId>
<artifactId>myproject.core</artifactId>
<type>jar</type>
<target>/apps/myproject-packages/application/install</target>
</embedded>
<embedded>
<groupId>com.myproject</groupId>
<artifactId>myproject.ui.apps</artifactId>
<type>zip</type>
<target>/apps/myproject-packages/application/install</target>
</embedded>
<embedded>
<groupId>com.myproject</groupId>
<artifactId>myproject.ui.content</artifactId>
<type>zip</type>
<target>/apps/myproject-packages/content/install</target>
</embedded>
</embeddeds>
</configuration>
</plugin>
Chaque <embedded> nécessite une <dependency> correspondante dans le même
pom.xml, pointant vers la version de l’artefact du module frère (normal
dans un reactor Maven, où ${project.version} garde tous les modules
synchronisés).
Pourquoi c’est important : un artefact déployable unique
Exécuter mvn clean install sur le module all produit un unique .zip
installable. Plutôt que d’uploader manuellement quatre ou cinq packages dans
le bon ordre vers Package Manager, ou de coordonner plusieurs étapes dans un
pipeline de déploiement, le pipeline Cloud Manager (ou tout script de
déploiement direct) upload et active un seul package. Ce package unique est
ce qui définit réellement quelle version du code et du contenu tourne sur une
instance AEM à un instant donné.
C’est aussi ce qui rend le build déterministe : l’agrégateur racine du projet
liste les modules sous <modules>, mais c’est le graphe de dépendances
déclaré dans chaque pom.xml — pas l’ordre de cette liste — que Maven
utilise pour calculer l’ordre réel de construction du reactor.
Où cela se manifeste en pratique
- Un build Cloud Manager échoue à cause d’un embed manquant : si vous
ajoutez un nouveau module ou oubliez simplement d’ajouter sa
<dependency>et son<embedded>dansall/pom.xml, le build peut passer sans erreur et produire un packageallparfaitement valide — sauf que votre nouveau bundle ou contenu n’atteint jamais l’instance, car il n’a jamais été embarqué. - Modifier
ui.contentn’oblige pas à reconstruirecore: commecoreetui.contentn’ont aucune dépendance de compilation entre eux (seulalldépend des deux), vous pouvez itérer sur le contenu d’exemple sans toucher au Java, et en CI vous pouvez restreindre le build avec-pl ui.content -amplutôt que de reconstruire le bundle à chaque changement. - “Mon composant n’apparaît pas après le déploiement” : avant de
soupçonner Sling ou le cache du dispatcher, vérifiez si le module qui le
contient est bien déclaré comme dépendance et comme
embeddeddans lepom.xmldeall. C’est la cause la plus fréquente d’un “ça compile mais ça n’apparaît pas”.