Adobe Experience Manager (AEM) è basato su uno stack software open-source ricco che espone molte API Java da utilizzare durante lo sviluppo. Questo articolo esplora le API principali e spiega quando e perché dovrebbero essere utilizzate.
AEM è basato su 4 set di API Java principali.
Adobe Experience Manager (AEM)
Apache SlingFramework Web
JCR (Apache Jackrabbit Oak)
OSGi (Apache Felix)
La regola generale è di preferire le API/astrazioni nel seguente ordine:
Se AEM fornisce un’API, preferiscila rispetto a Sling, JCR e OSGi. Se AEM non fornisce un’API, preferisci Sling rispetto a JCR e OSGi.
Questo ordine è una regola generale, il che significa che esistono delle eccezioni. Motivi accettabili per uscire da questa regola sono i seguenti:
Eccezioni ben note, come descritto di seguito.
La funzionalità richiesta non è disponibile in un’API di livello superiore.
Operando nel contesto del codice esistente (codice di prodotto personalizzato o AEM) che utilizza essa stessa un’API meno preferita e il costo per passare alla nuova API è ingiustificabile.
Le API di AEM forniscono astrazioni e funzionalità specifiche per i casi d’uso prodotti.
Ad esempio, le API di AEM PageManager e Page forniscono astrazioni per i nodi cq:Page
in AEM che rappresentano pagine web.
Anche se questi nodi sono disponibili tramite Sling API as Resources (Risorse) e API JCR as Nodes (Nodi), le API di AEM forniscono astrazioni per i casi d’uso comuni. L’utilizzo delle API AEM assicura un comportamento coerente tra AEM e il prodotto, nonché personalizzazioni ed estensioni per AEM.
Le API AEM hanno una preferenza intra-pacchetto, identificata dai seguenti pacchetti Java, in ordine di preferenza:
com.adobe.cq
com.adobe.granite
com.day.cq
com.adobe.cq
supporta i casi di utilizzo dei prodotti, mentre com.adobe.granite
supporta i casi di utilizzo di piattaforme cross-product, come i flussi di lavoro o le attività (utilizzati tra i prodotti: AEM Assets, Sites, ecc.).
com.day.cq
contiene API "originali". Queste API riguardano le astrazioni e le funzionalità principali che esistevano prima e/o intorno all'acquisizione di [!DNL Day CQ] da parte di Adobe. Queste API sono supportate e non devono essere evitate, a meno che com.adobe.cq
o com.adobe.granite
non forniscano un'alternativa (più recente).
Le nuove astrazioni come Content Fragments e Experience Fragments sono integrate nello spazio com.adobe.cq
anziché com.day.cq
come descritto di seguito.
AEM supporta più linguaggi di query. Le tre lingue principali sono JCR-SQL2, XPath e AEM Query Builder.
La preoccupazione più importante è mantenere un linguaggio di query coerente all'interno della base di codice, per ridurre la complessità e i costi da comprendere.
Tutti i linguaggi di query hanno effettivamente gli stessi profili di prestazioni, in quanto Apache Oak li trasferisce a JCR-SQL2 per l'esecuzione finale della query, e il tempo di conversione a JCR-SQL2 è trascurabile rispetto al tempo di query stesso.
L’API preferita è AEM Query Builder, che è l’astrazione di livello più alto e fornisce un’API affidabile per la costruzione, l’esecuzione e il recupero dei risultati per le query, e fornisce quanto segue:
Struttura semplice e parametrizzata delle query (parametri di query modellati come mappa)
Previsioni OOTB che supportano requisiti di query comuni
API estensibile, che consente lo sviluppo di predicati query personalizzati
JCR-SQL2 e XPath possono essere eseguiti direttamente tramite Sling e API JCR, restituendo risultati rispettivamente a Sling Resources o Nodi JCR.
L'API di AEM QueryBuilder perde un oggetto ResourceResolver. Per attenuare questa perdita, segui questo esempio di codice.
Sling Apacheè il framework web RESTful che supporta AEM. Sling fornisce routing delle richieste HTTP, modelli di nodi JCR come risorse, fornisce contesto di sicurezza e molto altro.
Sling Le API hanno il vantaggio aggiuntivo di essere create per l’estensione, il che significa che spesso è più facile e sicuro migliorare il comportamento delle applicazioni create utilizzando Sling le API rispetto alle API JCR meno estensibili.
Accesso ai nodi JCR come Sling Resources e accesso ai loro dati tramite ValueMaps.
Fornire un contesto di sicurezza tramite ResourceResolver.
Creazione e rimozione di risorse tramite i metodi create/move/copy/delete di ResourceResolver.
Aggiornamento delle proprietà tramite ModificableValueMap.
Blocchi predefiniti per l’elaborazione delle richieste di generazione
Blocchi predefiniti per l’elaborazione del lavoro asincrono
Le API JCR (Java Content Repository) 2.0 fanno parte di una specifica per le implementazioni JCR (nel caso di AEM, Apache Jackrabbit Oak). Tutte le implementazioni JCR devono essere conformi a queste API e implementarle; pertanto, è l’API di livello più basso per interagire con i contenuti di AEM.
Il JCR stesso è un datastore NoSQL gerarchico/basato su albero utilizzato da AEM come archivio dei contenuti. Il JCR dispone di una vasta gamma di API supportate, che vanno dal CRUD del contenuto alla query del contenuto. Nonostante questa solida API, è raro che siano preferite rispetto alle astrazioni di livello più alto di AEM e Sling .
Preferisci sempre le API JCR rispetto alle API Apache Jackrabbit Oak. Le API JCR sono per interagire con un archivio JCR, mentre le API Oak sono per implementare un archivio JCR.
Mentre JCR è l’archivio dei contenuti di AEM, le sue API NON sono il metodo preferito per interagire con il contenuto. Preferisce invece le API AEM (Pagina, Risorse, Tag, ecc.) o API di risorse Sling in quanto forniscono migliori astrazioni.
L'ampio utilizzo delle interfacce Session e Node delle API JCR in un'applicazione AEM è un odore di codice. Assicurati che le API Sling non siano utilizzate al loro posto.
Osservazione JCR (ascolto di eventi JCR)
Creazione di strutture dei nodi profondi
C'è poca sovrapposizione tra le API OSGi e le API di livello superiore (AEM, Sling e JCR), e la necessità di utilizzare le API OSGi è rara e richiede un alto livello di esperienza di sviluppo AEM.
OSGi definisce una specifica a cui tutti i contenitori OSGi devono implementare e conformarsi. L’implementazione OSGi di AEM, Apache Felix, fornisce anche diverse delle proprie API.
org.osgi
) rispetto alle API Apache Felix (org.apache.felix
).Annotazioni OSGi per dichiarare i servizi e i componenti OSGi.
API OSGi per l'annullamento/registrazione dinamica dei servizi/componenti OSGi](https://osgi.org/javadoc/r6/core/org/osgi/framework/package-summary.html) in-code.[
Di seguito sono riportate alcune eccezioni comuni alle regole definite in precedenza.
Preferisci com.day.cq.dam.api
su com.adobe.granite.asset.api
.
com.day.cq
Assets forniscono strumenti più gratuiti ai casi d’uso di AEM per la gestione delle risorse.@SlingServlet
@SlingFilter
Di seguito sono riportati utili snippet di codice Java che illustrano le best practice per i casi d’uso comuni che utilizzano API discusse. Questi snippet illustrano anche come passare da API meno preferite a API più preferite.
A partire da AEM 6.2, il Sling ResourceResolver è AutoClosable
in un'istruzione try-with-resources . Utilizzando questa sintassi, non è necessaria una chiamata esplicita a resourceResolver .close()
.
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
try (ResourceResolver resourceResolver = rrf.getResourceResolver(authInfo)) {
// Do work with the resourceResolver
} catch (LoginException e) { .. }
È possibile chiudere manualmente i ResourceResolver in un blocco finally
se non è possibile utilizzare la tecnica di chiusura automatica mostrata sopra.
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
ResourceResolver resourceResolver = null;
try {
resourceResolver = rrf.getResourceResolver(authInfo);
// Do work with the resourceResolver
} catch (LoginException e) {
...
} finally {
if (resourceResolver != null) { resourceResolver.close(); }
}
Resource resource = ResourceResolver.getResource("/path/to/the/resource");
Resource resource = resourceResolver.getResource(node.getPath());
DamUtil.resolveToAsset(..)
risolve tutte le risorse sotto dam:Asset
all’oggetto Risorsa spostandosi verso l’alto nella struttura, in base alle esigenze.
Asset asset = DamUtil.resolveToAsset(resource);
Per adattare una risorsa a una risorsa è necessario che la stessa sia il nodo dam:Asset
.
Asset asset = resource.adaptTo(Asset.class);
pageManager.getContainingPage(..)
risolve tutte le risorse sotto cq:Page
all’oggetto Page spostandosi verso l’alto nella struttura, in base alle esigenze.
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");
Per adattare una risorsa a una pagina è necessario che la risorsa stessa sia il nodo cq:Page
.
Page page = resource.adaptTo(Page.class);
Utilizzare i getters dell'oggetto Page per ottenere proprietà ben note (getTitle()
, getDescription()
, ecc.) e page.getProperties()
per ottenere il valore [cq:Page]/jcr:content
ValueMap per il recupero di altre proprietà.
Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());
L’API delle risorse fornisce metodi pratici per leggere le proprietà dal nodo [dam:Asset]/jcr:content/metadata
. Nota che non si tratta di un ValueMap, il secondo parametro (valore predefinito e casting automatico) non è supportato.
Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");
Quando le proprietà sono memorizzate in posizioni (proprietà o risorse relative) a cui le API AEM (Pagina, Risorsa) non possono accedere direttamente, per ottenere i dati è possibile utilizzare le Sling Risorse e ValueMaps .
ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");
In questo caso, potrebbe essere necessario convertire l’oggetto AEM in un Sling Resource per individuare in modo efficiente la proprietà o la risorsa secondaria desiderata.
Resource resource = page.adaptTo(Resource.class);
Resource resource = asset.adaptTo(Resource.class);
Utilizza Sling di ModificableValueMap per scrivere le proprietà sui nodi. Questo può solo scrivere sul nodo immediato (i percorsi delle proprietà relative non sono supportati).
Nota che la chiamata a .adaptTo(ModifiableValueMap.class)
richiede autorizzazioni di scrittura per la risorsa, altrimenti restituisce null.
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");
resource.getResourceResolver().commit();
Utilizza sempre PageManager per creare le pagine in quanto richiede un modello di pagina, è necessario per definire e inizializzare correttamente le pagine in AEM.
String templatePath = "/conf/my-app/settings/wcm/templates/content-page";
boolean autoSave = true;
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
pageManager.create("/content/parent/path", "my-new-page", templatePath, "My New Page Title", autoSave);
if (!autoSave) { resourceResolver.commit(); }
ResourceResolver supporta operazioni di base per la creazione di risorse. Durante la creazione di astrazioni di livello superiore (pagine AEM, risorse, tag, ecc.) utilizzare i metodi forniti dai rispettivi responsabili.
resourceResolver.create(parentResource, "my-node-name", new ImmutableMap.Builder<String, Object>()
.put("jcr:primaryType", "nt:unstructured")
.put("jcr:title", "Hello world")
.put("propertyName", "Other initial properties")
.build());
resourceResolver.commit();
ResourceResolver supporta la rimozione di una risorsa. Quando crei astrazioni di livello superiore (pagine AEM, risorse, tag, ecc.) utilizzare i metodi forniti dai rispettivi responsabili.
resourceResolver.delete(resource);
resourceResolver.commit();