Come un progetto Maven AEM costruisce davvero un pacchetto distribuibile
Come il pom.xml padre e i moduli core, ui.apps, ui.content, ui.config e all di un progetto AEM si combinano per produrre un unico pacchetto distribuibile, e perché il modulo all è la vera unità di deployment.
Un progetto AEM generato con l’AEM Project Archetype non produce un solo
artefatto: ne produce mezza dozzina. La confusione tipica di chi arriva su un
progetto AEM per la prima volta non è “cosa fa Maven”, ma “quale di tutti
questi pom.xml è quello effettivamente distribuito sull’istanza”. La
risposta breve: quasi nessuno singolarmente — ciò che viene distribuito è il
risultato di un modulo specifico, all, che assembla tutti gli altri.
La struttura multimodulo generata dall’archetype
Generando un progetto con com.adobe.aem:aem-project-archetype si ottiene un
pom.xml padre (aggregatore) nella radice e diversi moduli figli, ciascuno
con il proprio pom.xml e il proprio tipo di packaging. I moduli standard
sono:
core— un bundle OSGi (jar) con servizi, listener, scheduler, Sling Model e servlet: tutto il codice Java del progetto.ui.apps— un content-package distribuito in/apps: HTL, client library e definizioni dei componenti.ui.content— un content-package con contenuto mutabile di esempio (pagine, configurazione del sito sotto/contente/conf).ui.config— un content-package con configurazioni OSGi specifiche per runmode e script Repo-init.ui.frontend— una build frontend (npm/webpack) il cui output (JS/CSS compilati) finisce impacchettato dentroui.apps.all— un content-package “contenitore” senza contenuto proprio: si limita a incorporare gli artefatti dei moduli precedenti.
I dettagli interni di ui.apps e ui.frontend meritano un articolo
dedicato — qui interessa come tutti i moduli si incastrano tra loro per
produrre il pacchetto finale, non come funziona internamente ciascuno di
essi.
Due tipi di packaging: bundle vs. content-package
core è l’unico modulo con packaging jar normale: è un bundle OSGi come
qualsiasi altro, compilato e impacchettato con il consueto plugin per
bundle. Gli altri moduli che producono contenuto (ui.apps, ui.content,
ui.config, all) usano il packaging content-package, costruito dal
filevault-package-maven-plugin (org.apache.jackrabbit). Questo
plugin ha sostituito il vecchio content-package-maven-plugin di Adobe/Day
ed è attualmente l’unico supportato su AEM as a Cloud Service.
Ogni modulo di tipo content-package dichiara inoltre un packageType nella
configurazione del plugin:
application→ui.apps(codice immutabile, distribuito in/apps).content→ui.contenteui.config(contenuto e configurazione mutabili).container→all(incorpora solo altri pacchetti, senza contenuto o codice proprio).
Questa classificazione non è cosmetica: la validazione dei pacchetti di
Cloud Manager la usa per respingere build in cui, ad esempio, un pacchetto
marcato application tocca percorsi /content che non gli competono.
Il modulo all: come viene assemblato il pacchetto finale
Il pom.xml di all dichiara dipendenze di tipo zip (o jar per core)
verso gli altri moduli, e la configurazione del
filevault-package-maven-plugin usa <embeddeds> per indicare in quale
percorso di installazione ciascuno debba essere incorporato all’interno del
pacchetto contenitore. Questo è l’approccio attuale; il vecchio meccanismo
<subPackages> è deprecato.
<!-- all/pom.xml (frammento) -->
<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>
Ogni <embedded> richiede una <dependency> corrispondente nello stesso
pom.xml, che punta alla versione dell’artefatto del modulo fratello
(normale in un reactor Maven, dove ${project.version} mantiene tutti i
moduli sincronizzati).
Perché questo è importante: un unico artefatto distribuibile
Eseguire mvn clean install sul modulo all produce un unico .zip
installabile. Invece di caricare manualmente quattro o cinque pacchetti
nell’ordine corretto su Package Manager, o coordinare più passaggi in una
pipeline di deployment, la pipeline di Cloud Manager (o qualsiasi script di
deployment diretto) carica e attiva un solo pacchetto. Quel pacchetto unico è
ciò che definisce realmente quale versione di codice e contenuto sta girando
su un’istanza AEM in un dato momento.
Questo è anche ciò che rende la build deterministica: l’aggregatore radice
del progetto elenca i moduli sotto <modules>, ma è il grafo delle
dipendenze dichiarato in ciascun pom.xml — non l’ordine di quell’elenco —
che Maven usa per calcolare l’ordine effettivo di build del reactor.
Dove si presenta in pratica
- Una build di Cloud Manager fallisce per un embed mancante: se aggiungi
un nuovo modulo o semplicemente dimentichi di aggiungere la sua
<dependency>e il suo<embedded>inall/pom.xml, la build può passare senza errori e produrre un pacchettoallperfettamente valido — solo che il tuo nuovo bundle o contenuto non arriva mai sull’istanza, perché non è mai stato incorporato. - Modificare
ui.contentnon obbliga a ricompilarecore: dato checoreeui.contentnon hanno alcuna dipendenza di compilazione tra loro (soloalldipende da entrambi), puoi iterare sul contenuto di esempio senza toccare Java, e in CI puoi limitare la build con-pl ui.content -aminvece di ricostruire il bundle a ogni modifica. - “Il mio componente non compare dopo il deployment”: prima di
sospettare di Sling o della cache del dispatcher, verifica se il modulo
che lo contiene è effettivamente dichiarato come dipendenza e come
embeddednelpom.xmldiall. È la causa più comune di “compila bene ma non si vede”.