Come Sling risolve una richiesta verso un componente AEM
Come Sling trasforma un URL in una Resource e poi in un componente AEM tramite sling:resourceType, i search path /apps e /libs, e perché sling:resourceSuperType è il modo corretto per estendere i Core Components.
Quando AEM renderizza una pagina, ciò che accade realmente sotto è una
risoluzione Sling in due passaggi: prima un URL diventa una Resource,
poi quella Resource diventa un componente — uno script HTL o un servlet
— che decide quale HTML viene restituito. Capire questa pipeline spiega
perché /apps può prevalere su /libs, perché i Core Components si
estendono con sling:resourceSuperType invece di essere sovrascritti
direttamente, e perché un selettore sbagliato può rompere la cache del
Dispatcher.
Dall’URL alla Resource
Quando arriva una richiesta come /content/miosito/it/home.html, Sling
divide il percorso in resource path, selettori ed estensione, e usa il
ResourceResolver per mappare /content/miosito/it/home su un nodo JCR
reale. In un progetto AEM quel nodo è di solito una pagina (cq:Page), ma
ciò che conta davvero per il rendering è il suo nodo jcr:content (o
quello di un componente figlio), perché è lì che vive la proprietà
sling:resourceType.
Da Resource a componente: sling:resourceType
sling:resourceType non è un percorso assoluto: è un percorso relativo
che Sling cerca in un elenco ordinato di search path. Per esempio, se un
componente ha:
sling:resourceType = "mioprogetto/components/hero"
Sling cerca uno script (.html, compilato da HTL a Java sotto il
cofano) o un servlet il cui percorso, relativo a uno dei search path,
corrisponde a mioprogetto/components/hero.
I search path di Sling: perché /apps viene prima di /libs
La configurazione predefinita di Sling (resource.resolver.searchpath) è
["/apps", "/libs"]. Questo significa che, per lo stesso
sling:resourceType, Sling prova sempre prima lo script trovato sotto
/apps rispetto a quello trovato sotto /libs. Questo è il meccanismo
tecnico dietro il classico “overlay” di AEM: se una risorsa di prodotto
vive in /libs/qualcosa/componente e tu metti uno script in
/apps/qualcosa/componente, il tuo vince.
Su AEM as a Cloud Service, /libs è completamente immutabile: non ci
distribuirai mai nulla. Questo overlay via search path è ancora reale per
certe risorse di piattaforma che vivono ancora sotto /libs (parti di
Granite UI o della console OSGi, ad esempio), ma non è il meccanismo da
usare per estendere gli AEM Core Components — ed è un errore comune.
Perché i Core Components non si overlay-ano: sling:resourceSuperType
Gli AEM Core Components (core/wcm/components/...) vengono distribuiti
come dipendenza del progetto e finiscono installati sotto /apps, non
sotto /libs. Dato che sono già in /apps, non puoi “batterli” mettendo
qualcosa sullo stesso sling:resourceType sotto /apps senza sostituire
completamente il componente di prodotto — perdendo così qualsiasi
miglioramento futuro rilasciato da Adobe.
Il modello corretto è l’ereditarietà dei componenti tramite
sling:resourceSuperType:
<!-- /apps/mioprogetto/components/text/.content.xml -->
<jcr:root
jcr:primaryType="cq:Component"
jcr:title="Text"
sling:resourceSuperType="core/wcm/components/text/v2/text"/>
Con questo, il tuo componente mioprogetto/components/text eredita tutto
ciò che non sovrascrivi esplicitamente (dialog, script HTL, Sling Model)
dal resourceSuperType. Se il tuo script HTL non definisce un certo
blocco, Sling risale la catena di resourceSuperType finché non lo trova
nel Core Component. Questo è ciò che ti permette di personalizzare solo il
10% che ti serve cambiare senza duplicare il restante 90%.
Selettori ed estensione cambiano anch’essi lo script scelto
I selettori e l’estensione della richiesta (.html, .json, un
selettore come .print) entrano anch’essi nella risoluzione: Sling cerca
prima uno script specifico per quella combinazione (text.print.html)
prima di ricadere sullo script predefinito (text.html). È esattamente
ciò su cui si basa il JSON Exporter di AEM (.model.json) per servire la
rappresentazione Sling Model di un componente senza toccare il normale
script di rendering.
Dove questo conta in un vero progetto AEM
- Debug del rendering: se un componente non usa il tuo script,
sospetta prima del search path (il tuo script è davvero sotto
/apps?) e poi disling:resourceSuperType(il tuo componente eredita dalresourceTypegiusto, e dalla versione giusta — ad es.text/v2/text, nontext/v1/text?). - Aggiornamenti dei Core Components: dato che estendi per ereditarietà e non per overlay, aggiornare la versione dei Core Components non ti costringe a riconciliare un fork completo del componente.
- Cache del Dispatcher: ogni combinazione distinta di selettore/estensione usata dal tuo progetto va considerata nelle regole di cache del Dispatcher; un selettore “di debug” dimenticato in produzione può generare voci di cache inutili o, peggio, contenuto non cacheable dove invece dovrebbe esserlo.