Cómo un proyecto Maven de AEM construye un paquete desplegable
Cómo el pom.xml padre y los módulos core, ui.apps, ui.content, ui.config y all de un proyecto AEM se combinan para producir un único paquete desplegable, y por qué el módulo all es la unidad de despliegue real.
Un proyecto AEM generado con el AEM Project Archetype no produce un único
artefacto: produce media docena. La confusión habitual de quien llega nuevo a
un proyecto AEM no es “qué hace Maven”, sino “cuál de todos estos pom.xml
es el que realmente se despliega en la instancia”. La respuesta corta es: casi
ninguno por separado — lo que se despliega es el resultado de un módulo
concreto, all, que ensambla a los demás.
El diseño multimódulo que genera el arquetipo
Al generar un proyecto con com.adobe.aem:aem-project-archetype, obtienes un
pom.xml padre (agregador) en la raíz y varios módulos hijos, cada uno con su
propio pom.xml y su propio tipo de empaquetado. Los módulos estándar son:
core— bundle OSGi (jar) con servicios, listeners, schedulers, Sling Models y servlets: todo el código Java del proyecto.ui.apps— content-package que se despliega en/apps: HTL, client libraries y definiciones de componentes.ui.content— content-package con contenido mutable de ejemplo (páginas, configuración de sitio en/contenty/conf).ui.config— content-package con configuraciones OSGi específicas por runmode y scripts Repo-init.ui.frontend— build de frontend (npm/webpack) cuyo resultado (JS/CSS compilados) termina empaquetado dentro deui.apps.all— un content-package “contenedor” sin contenido propio: solo embebe los artefactos de los módulos anteriores.
Los detalles internos de ui.apps y ui.frontend merecen su propio
artículo — aquí interesa cómo encajan todos los módulos entre sí para
producir el paquete final, no cómo funciona cada uno por dentro.
Dos tipos de empaquetado: bundle vs. content-package
core es el único módulo con empaquetado jar normal: es un bundle OSGi
como cualquier otro, compilado y empaquetado con el plugin de bundles
habitual. El resto de módulos que producen contenido (ui.apps,
ui.content, ui.config, all) usan el empaquetado content-package,
construido por el filevault-package-maven-plugin (org.apache.jackrabbit).
Este plugin sustituyó al antiguo content-package-maven-plugin de Adobe/Day
y es actualmente el único soportado en AEM as a Cloud Service.
Cada módulo de tipo content-package declara además un packageType en la
configuración del plugin:
application→ui.apps(código inmutable, va a/apps).content→ui.contentyui.config(contenido y configuración mutables).container→all(solo embebe otros paquetes, no tiene contenido ni código propio).
Esta clasificación no es cosmética: la validación de paquetes de Cloud
Manager la usa para rechazar builds donde, por ejemplo, un paquete marcado
como application toca rutas de /content que no le corresponden.
El módulo all: cómo se ensambla el paquete final
El pom.xml de all declara dependencias de tipo zip (o jar para
core) sobre los demás módulos, y la configuración del
filevault-package-maven-plugin usa <embeddeds> para indicar en qué ruta
de instalación debe embeberse cada uno dentro del paquete contenedor. Esta es
la forma actual de hacerlo; el mecanismo antiguo, <subPackages>, está
deprecado.
<!-- all/pom.xml (fragmento) -->
<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>
Cada <embedded> necesita su correspondiente <dependency> en la misma
pom.xml, apuntando a la versión del artefacto del módulo hermano (normal en
un reactor Maven, donde ${project.version} mantiene todos los módulos
sincronizados).
Por qué esto importa: un único artefacto desplegable
El resultado de mvn clean install en el módulo all es un único .zip
instalable. En lugar de subir manualmente cuatro o cinco paquetes en el orden
correcto a Package Manager, o coordinar múltiples pasos en un pipeline de
despliegue, el pipeline de Cloud Manager (o cualquier script de despliegue
directo) sube y activa un solo paquete. Ese único paquete es el que realmente
define qué versión de código y contenido está corriendo en una instancia AEM
en un momento dado.
Esto también es lo que hace determinista el build: el agregador raíz del
proyecto lista los módulos en <modules>, pero es el grafo de dependencias
declarado en cada pom.xml — no el orden de esa lista — lo que Maven usa
para calcular el orden real de construcción del reactor.
Dónde aparece esto en la práctica
- Un build de Cloud Manager falla por un embed que falta: si añades un
módulo nuevo o simplemente te olvidas de añadir su
<dependency>y su<embedded>enall/pom.xml, el build puede pasar perfectamente y el paqueteallse genera sin errores — solo que tu bundle o tu contenido nuevo nunca llega a la instancia, porque nunca se embebió. - Cambiar
ui.contentno obliga a recompilarcore: comocoreyui.contentno tienen dependencia de compilación entre sí (soloalldepende de ambos), puedes iterar sobre contenido de ejemplo sin tocar Java, y en CI puedes acotar el build con-pl ui.content -ampara no reconstruir el bundle en cada cambio. - “Mi componente no aparece tras el despliegue”: antes de sospechar de
Sling o de la caché del dispatcher, comprueba si el módulo que lo contiene
está realmente declarado como dependencia y como
embeddeden elpom.xmldeall. Es la causa más común de “compiló bien pero no se ve”.