Cómo Sling resuelve una petición hasta un componente de AEM
Cómo Sling convierte una URL en un Resource y luego en un componente AEM a través de sling:resourceType, las search paths /apps y /libs, y por qué sling:resourceSuperType es el patrón correcto para extender los Core Components.
Cuando AEM renderiza una página, lo que realmente ocurre por debajo es una
resolución de Sling en dos pasos: primero una URL se convierte en un
Resource, y después ese Resource se convierte en un componente —un
script HTL o un servlet— que decide qué HTML se devuelve. Entender este
pipeline explica por qué /apps puede sobreescribir /libs, por qué los
Core Components se extienden con sling:resourceSuperType en lugar de
sobrescribirlos directamente, y por qué un selector mal puesto puede romper
la caché del Dispatcher.
De la URL al Resource
Cuando llega una petición como /content/mysite/es/inicio.html, Sling
separa la ruta en resource path, selectores y extensión, y usa el
ResourceResolver para mapear /content/mysite/es/inicio a un nodo JCR
real. En un proyecto AEM ese nodo suele ser una página (cq:Page), pero lo
que realmente importa para el renderizado es su nodo jcr:content (o el de
un componente hijo), porque ahí es donde vive la propiedad
sling:resourceType.
De Resource a componente: sling:resourceType
sling:resourceType no es una ruta absoluta: es una ruta relativa que
Sling busca dentro de una lista ordenada de search paths. Por ejemplo, si
un componente tiene:
sling:resourceType = "myproject/components/hero"
Sling busca un script (.html, .htl compilado a Java bajo el capó) o un
servlet cuya ruta, relativa a alguno de los search paths, coincida con
myproject/components/hero.
Las search paths de Sling: por qué /apps va antes que /libs
La configuración por defecto de Sling (resource.resolver.searchpath)
es ["/apps", "/libs"]. Esto significa que, ante el mismo
sling:resourceType, Sling siempre prueba primero el script que encuentre
bajo /apps antes que el que encuentre bajo /libs. Este es el mecanismo
técnico detrás del “overlay” clásico de AEM: si un recurso del producto
vive en /libs/algo/componente y tú colocas un script en
/apps/algo/componente, el tuyo gana.
En AEM as a Cloud Service, /libs es completamente inmutable: nunca vas a
desplegar nada ahí. Este overlay por search path sigue siendo real para
ciertos recursos de plataforma que todavía viven bajo /libs (por ejemplo,
partes de Granite UI o de la consola de OSGi), pero no es el mecanismo
que debes usar para extender los AEM Core Components — y ese es un error
común.
Por qué los Core Components no se overlean: sling:resourceSuperType
Los AEM Core Components (core/wcm/components/...) se entregan como parte
de las dependencias de tu proyecto y terminan instalados bajo /apps, no
bajo /libs. Como ya están en /apps, no puedes “ganarles” poniendo algo
en el mismo sling:resourceType bajo /apps sin sustituir por completo el
componente del producto — perdiendo cualquier mejora futura que Adobe
publique.
El patrón correcto es la herencia de componentes vía
sling:resourceSuperType:
<!-- /apps/myproject/components/text/.content.xml -->
<jcr:root
jcr:primaryType="cq:Component"
jcr:title="Text"
sling:resourceSuperType="core/wcm/components/text/v2/text"/>
Con esto, tu componente myproject/components/text hereda todo lo que no
sobrescribas explícitamente (diálogo, script HTL, Sling:Model) del
resourceSuperType. Si tu script HTL no define, por ejemplo, un bloque
concreto, Sling sube por la cadena de resourceSuperType hasta
encontrarlo en el Core Component. Esto es lo que permite personalizar solo
el 10% que necesitas cambiar sin duplicar el 90% restante.
Selectores y extensión también cambian el script elegido
Los selectores y la extensión de la petición (.html, .json, un
selector como .print) también entran en la resolución: Sling busca
primero un script específico para esa combinación
(text.print.html) antes de caer al script por defecto
(text.html). Esto es exactamente lo que usa el JSON Exporter de AEM
(.model.json) para servir la representación Sling Model de un
componente sin tocar el script de renderizado normal.
Dónde esto importa en un proyecto AEM real
- Debugging de renderizado: si un componente no usa tu script,
sospecha primero de la search path (¿tu script realmente está bajo
/apps?) y después desling:resourceSuperType(¿tu componente hereda delresourceTypecorrecto y con la versión correcta, p. ej.text/v2/texty notext/v1/text?). - Actualizaciones de Core Components: como extiendes por herencia y no por overlay, actualizar la versión de los Core Components no te obliga a reconciliar un fork completo del componente.
- Caché del Dispatcher: cada combinación distinta de selector/extensión que tu proyecto use debe considerarse en las reglas de caché del Dispatcher; un selector “de debug” olvidado en producción puede generar entradas de caché innecesarias o, peor, contenido no cacheable donde debería serlo.