Best Practices für Java™-APIs
Adobe Experience Manager (AEM) basiert auf einer umfassenden Open-Source-Software-Technologie, über die zahlreiche Java™-APIs zur Verwendung während der Entwicklung bereitgestellt werden. In diesem Artikel werden die wichtigsten APIs sowie deren Verwendungszeitpunkte und -zwecke erläutert.
AEM basiert auf vier primären Java™-API-Sets.
-
Adobe Experience Manager (AEM)
- Produktabstraktionen wie Seiten, Assets, Workflows usw.
-
Apache Sling Web-Framework
- REST- und ressourcenbasierte Abstraktionen wie Ressourcen, Wertzuordnungen und HTTP-Anfragen.
-
JCR (Apache Jackrabbit Oak)
- Daten- und Inhaltsabstraktionen wie Knoten, Eigenschaften und Sitzungen.
-
OSGi (Apache Felix)
- OSGi-Programm-Container-Abstraktionen wie Services und (OSGi-) Komponenten.
Die „Faustregel“ für Java™-API-Voreinstellungen
Die allgemeine Regel besteht darin, die APIs/Abstraktionen in der folgenden Reihenfolge vorzuziehen:
- AEM
- Sling
- JCR
- OSGi
Wenn eine API von AEM bereitgestellt wird, sollten Sie sie Sling, JCR und OSGi vorziehen. Wenn AEM keine API bereitstellt, sollten Sie Sling den JCR- und OSGi-APIs vorziehen.
Diese Reihenfolge ist nur eine allgemeine Regel, d. h. es gibt Ausnahmen. Folgende Gründe können dafür sprechen, von dieser Regel abzuweichen:
-
Bekannte Ausnahmen, wie unten beschrieben.
-
In einer API auf höherer Ebene ist die erforderliche Funktionalität nicht verfügbar.
-
Die Verwendung im Kontext von vorhandenem Code (benutzerdefinierter oder AEM-Produkt-Code), der selbst eine weniger bevorzugte API verwendet, und die Kosten für den Wechsel zu einer neuen API wären nicht zu rechtfertigen.
- Es ist besser, die API der unteren Ebene konsistent zu verwenden, als eine Mischung zu erstellen.
AEM-APIs
AEM-APIs bieten Abstraktionen und Funktionen speziell für produktspezifische Anwendungsfälle.
Zum Beispiel bieten die PageManager- und Seiten-APIs von AEM Abstraktionen für cq:Page
-Knoten in AEM, die Web-Seiten darstellen.
Diese Knoten sind über Sling-APIs als Ressourcen und JCR-APIs als Knoten verfügbar. AEM-APIs bieten hingegen Abstraktionen für häufige Anwendungsfälle. Durch die Verwendung der AEM-APIs wird ein konsistentes Verhalten zwischen AEM (Produkt) und den Anpassungen und Erweiterungen zu AEM sichergestellt.
com.adobe.*-APIs vs. com.day.*-APIs
AEM-APIs verfügen über eine paketinterne Präferenz, die durch die folgenden Java™-Pakete (in der Reihenfolge ihrer Präferenz) gekennzeichnet ist:
com.adobe.cq
com.adobe.granite
com.day.cq
Das com.adobe.cq
-Paket unterstützt Anwendungsfälle für Produkte, während com.adobe.granite
Plattform-Anwendungsfälle wie Workflows oder Aufgaben unterstützt, die produktübergreifend verwendet werden – AEM Assets, Sites usw.
Das com.day.cq
-Paket enthält „Original“-APIs. Bei diesen APIs geht es um zentrale Abstraktionen und Funktionen, die es vor und/oder rund um den Erwerb von Day CQ durch Adobe gab. Diese APIs werden zwar, unterstützt sollten aber vermieden werden, es sei denn, com.adobe.cq
oder com.adobe.granite
-Pakete bieten KEINE (neuere) Alternative.
Neue Abstraktionen wie Content Fragments und Experience Fragments werden eher im Bereich com.adobe.cq
als im unten beschriebenen Bereich com.day.cq
entwickelt.
Abfrage-APIs
AEM unterstützt mehrere Abfragesprachen. Die drei Hauptsprachen sind JCR-SQL2, XPath und AEM Query Builder.
Das wichtigste Anliegen ist die Beibehaltung einer konsistenten Abfragesprache über die gesamte Code-Basis hinweg, um Komplexität und Kosten zu reduzieren.
Alle Abfragesprachen haben im Grunde dieselben Leistungsprofile, da Apache Oak sie für die endgültige Ausführung der Abfrage in JCR-SQL2 überträgt. Die Zeit für die Konvertierung in JCR-SQL2 ist dabei im Vergleich zur Abfragezeit selbst unerheblich.
Die bevorzugte API ist AEM Query Builder, da dieser Abstraktion auf höchster Ebene und eine robuste API zum Erstellen, Ausführen und Abrufen von Ergebnissen für Abfragen bietet sowie Folgendes bereitstellt:
-
Einfache, parametrisierte Abfrageerstellung (Abfrageparameter, modelliert als Karte)
-
Native Java™-API und HTTP-APIs
-
AEM-Prädikate, die allgemeine Abfrageanforderungen unterstützen
-
Erweiterbare API, die die Entwicklung benutzerdefinierter Abfrageprädikate ermöglichen
-
JCR-SQL2 und XPath, direkt über Sling und JCR-APIs ausführbar, mit Ergebnisrückgabe als Sling Ressourcen bzw. JCR-Knoten
Sling-APIs
Apache Sling ist das RESTful-Webframework, das AEM unterstützt. Sling bietet HTTP-Anfrage-Routing, modelliert JCR-Knoten als Ressourcen, bietet einen Sicherheitskontext und vieles mehr.
Sling-APIs haben den zusätzlichen Vorteil, dass sie auf Erweiterungen ausgelegt sind. Dies bedeutet, dass es häufig einfacher und sicherer ist, das Verhalten von mit Sling-APIs erstellten Anwendungen zu erweitern als das von JCR-APIs, die sich nicht so gut erweitern lassen.
Häufige Anwendungsbereiche von Sling-APIs
-
Zugreifen auf JCR-Knoten als Sling Resources und Zugreifen auf ihre Daten über ValueMaps
-
Bereitstellen eines Sicherheitskontexts über ResourceResolver
-
Erstellen und Entfernen von Ressourcen über die ResourceResolver-Methoden zum Erstellen/Verschieben/Kopieren/Löschen
-
Aktualisieren von Eigenschaften über ModifiableValueMap
-
Erstellen von Bausteinen für die Anfrageverarbeitung
-
Bausteine für die asynchrone Aufgabenverarbeitung
JCR-APIs
Die JCR (Java™ Content Repository) 2.0-APIs sind Teil einer Spezifikation für JCR-Implementierungen (im Falle von AEM: Apache Jackrabbit Oak). Alle JCR-Implementierungen müssen diesen APIs entsprechen und diese implementieren. Daher sind dies die APIs der niedrigsten Ebene für die Interaktion mit AEM-Inhalten.
Das JCR selbst ist ein hierarchischer/baumbasierter NoSQL-Datenspeicher, der von AEM als Content-Repository verwendet wird. Das JCR verfügt über eine Vielzahl unterstützter APIs, ob für CRUD-Vorgänge oder Abfragen von Inhalten. Trotz der Robustheit dieser APIs werden sie nur selten gegenüber den AEM- und Sling-Abstraktionen höherer Ebene bevorzugt.
Ziehen Sie JCR-APIs immer den Apache Jackrabbit Oak-APIs vor. JCR-APIs werden zur Interaktion mit einem JCR-Repository eingesetzt, Oak-APIs zur Implementierung eines JCR-Repositorys.
Häufige Fehlannahmen über JCR-APIs
Beim JCR handelt es sich zwar um das Content-Repository von AEM, seine APIs sind jedoch NICHT die bevorzugte Methode zum Interagieren mit Inhalten. Bevorzugen Sie stattdessen AEM-APIs (Seite, Assets, Tag usw.) oder Sling-Ressourcen-APIs, da diese bessere Abstraktionen bieten.
Häufige Anwendungsbereiche von JCR-APIs
-
JCR-Beobachtung (Überwachen auf JCR-Ereignisse)
-
Erstellen von tiefen Knotenstrukturen
OSGi-APIs
Es gibt kaum Überschneidungen zwischen den OSGi-APIs und übergeordneten APIs (AEM, Sling und JCR). Außerdem müssen OSGi-APIs nur selten verwendet werden, was dann aber im Fall der Fälle ein hohes Maß an AEM-Entwicklungskompetenz voraussetzt.
OSGi- und Apache Felix-APIs
OSGi definiert eine Spezifikation, die alle OSGi-Container implementieren und erfüllen müssen. Die OSGi-Implementierung von AEM, Apache Felix, stellt zudem mehrere eigene APIs bereit.
- Ziehen Sie OSGi-APIs (
org.osgi
) den Apache Felix-APIs (org.apache.felix
) vor.
Häufige Anwendungsbereiche von OSGi-APIs
-
OSGi-Anmerkungen zum Deklarieren von OSGi-Diensten und -Komponenten
- Bevorzugen Sie OSGi Declarative Services (DS) 1.2-Anmerkungen gegenüber Felix-SCR-Anmerkungen zum Deklarieren von OSGi-Diensten und -Komponenten.
-
OSGi-APIs für dynamisches Registrieren/Aufheben der Registrierung von OSGi-Diensten/-Komponenten im Code
- Verwenden Sie vorzugsweise OSGi DS 1.2-Anmerkungen, wenn ein bedingtes Management von OSGi-Diensten/-Komponenten nicht erforderlich ist (das ist meistens der Fall).
Ausnahmen von der Regel
Im Folgenden finden Sie allgemeine Ausnahmen von den oben definierten Regeln.
OSGi-APIs
Bei OSGi-Abstraktionen niedriger Ebene, z. B. beim Definieren oder Lesen in OSGi-Komponenteneigenschaften, werden die neueren von org.osgi
bereitgestellten Abstraktionen gegenüber Sling-Abstraktionen höherer Ebene bevorzugt. Die konkurrierenden Sling-Abstraktionen wurden nicht als @Deprecated
markiert und schlagen die Alternative org.osgi
vor.
Beachten Sie außerdem, dass die Definition des OSGi-Konfigurationsknotens das Format cfg.json
gegenüber sling:OsgiConfig
bevorzugt.
AEM-Asset-APIs
-
Bevorzugen Sie
com.day.cq.dam.api
gegenübercom.adobe.granite.asset.api
.- Die
com.day.cq
-Asset-APIs bieten umfassendere Tools für AEM-Asset Management-Anwendungsfälle. - Die Granite Assets-APIs unterstützen Asset-Management-Anwendungsfälle auf niedriger Ebene (Version, Relationen).
- Die
Abfrage-APIs
- AEM QueryBuilder bietet keine Unterstützung für bestimmte Abfragefunktionen wie Vorschläge, Rechtschreibprüfung und Indexhinweise, neben anderen weniger gängigen Funktionen. Für eine Abfrage mit diesen Funktionen ist JCR-SQL2 zu bevorzugen.
Sling-Servlet-Registrierung sling-servlet-registration
- Bevorzugen Sie zur Sling-Servlet-Registrierung OSGi DS 1.2-Anmerkungen mit @SlingServletResourceTypes gegenüber
@SlingServlet
.
Sling-Filterregistrierung sling-filter-registration
- Bevorzugen Sie zur Sling-Filterregistrierung OSGi DS 1.2-Anmerkungen mit @SlingServletFilter gegenüber
@SlingFilter
.
Nützliche Codesnippets
Im Folgenden finden Sie hilfreiche Java™-Codesnippets, die Best Practices für häufige Anwendungsfälle mit den besprochenen APIs veranschaulichen. Diese Snippets zeigen auch, wie Sie von weniger bevorzugten APIs zu bevorzugten APIs wechseln können.
JCR-Sitzung an Sling ResourceResolver
Automatisches Schließen von Sling ResourceResolver
Seit AEM 6.2 ist Sling ResourceResolver in einer Anweisung try-with-resources als AutoClosable
festgelegt. Mithilfe dieser Syntax ist ein expliziter Aufruf an resourceResolver .close()
nicht erforderlich.
@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) { .. }
Manuell geschlossener Sling ResourceResolver
ResourceResolver muss manuell in einem finally
-Block geschlossen werden, wenn die oben dargestellte Technik zum automatischen Schließen nicht verwendet werden kann.
@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(); }
}
JCR-Pfad zu Sling Resource
Resource resource = ResourceResolver.getResource("/path/to/the/resource");
JCR-Knoten zu Sling Resource
Resource resource = resourceResolver.getResource(node.getPath());
Sling Resource zu AEM-Asset
Empfohlener Ansatz
Die Funktion DamUtil.resolveToAsset(..)
löst alle Ressourcen unter dam:Asset
zum Asset-Objekt auf, indem die Struktur ggf. nach oben durchgegangen wird.
Asset asset = DamUtil.resolveToAsset(resource);
Alternativer Ansatz
Die Anpassung einer Ressource an ein Asset setzt voraus, dass die Ressource selbst der dam:Asset
-Knoten ist.
Asset asset = resource.adaptTo(Asset.class);
Sling-Ressource zur AEM-Seite
Empfohlener Ansatz
pageManager.getContainingPage(..)
löst alle Ressourcen unter cq:Page
zum Seitenobjekt auf, indem die Struktur ggf. nach oben durchgegangen wird.
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");
Alternativer Ansatz alternative-approach-1
Die Anpassung einer Ressource an eine Seite setzt voraus, dass die Ressource selbst der cq:Page
-Knoten ist.
Page page = resource.adaptTo(Page.class);
Lesen von AEM-Seiteneigenschaften
Verwenden Sie die Getter des Seitenobjekts, um bekannte Eigenschaften (getTitle()
, getDescription()
usw.) abzurufen, und page.getProperties()
, um die ValueMap von [cq:Page]/jcr:content
zum Abrufen anderer Eigenschaften zu erhalten.
Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());
Lesen der Metadateneigenschaften von AEM-Assets
Die Asset-API bietet praktische Methoden zum Lesen von Eigenschaften aus dem Knoten [dam:Asset]/jcr:content/metadata
. Dies ist keine ValueMap, und der zweite Parameter (Standardwert und automatische Typumwandlung) wird nicht unterstützt.
Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");
Lesen von Sling Resource-Eigenschaften read-sling-resource-properties
Wenn Eigenschaften an Stellen (Eigenschaften oder relativen Ressourcen) gespeichert werden, auf die die AEM-APIs (Seite, Asset) nicht direkt zugreifen können, können die Sling-Ressourcen und ValueMaps zum Abrufen der Daten verwendet werden.
ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");
In diesem Fall muss das AEM-Objekt möglicherweise in eine Sling Resource umgewandelt werden, um die gewünschte Eigenschaft oder Unterressource auf effiziente Weise zu finden.
AEM-Seite zu Sling Resource
Resource resource = page.adaptTo(Resource.class);
AEM-Asset zu Sling Resource
Resource resource = asset.adaptTo(Resource.class);
Schreiben von Eigenschaften mit ModifiableValueMap von Sling
Verwenden Sie ModifiableValueMap von Sling, um Eigenschaften in Knoten zu schreiben. Dabei kann nur in den unmittelbaren Knoten geschrieben werden (relative Eigenschaftenpfade werden nicht unterstützt).
Der Aufruf an .adaptTo(ModifiableValueMap.class)
erfordert Schreibberechtigungen für die Ressource. Ansonsten wird null zurückgegeben.
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");
resource.getResourceResolver().commit();
Erstellen einer AEM-Seite
Verwenden Sie immer PageManager, um Seiten zu erstellen, da eine Seitenvorlage erforderlich ist, um Seiten in AEM ordnungsgemäß zu definieren und zu initialisieren.
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(); }
Erstellen einer Sling-Ressource
ResourceResolver unterstützt grundlegende Vorgänge zum Erstellen von Ressourcen. Verwenden Sie beim Erstellen von Abstraktionen höherer Ebene (AEM-Seiten, Assets, Tags usw.) die vom entsprechenden Manager bereitgestellten Methoden.
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();
Löschen einer Sling-Ressource
ResourceResolver unterstützt das Entfernen einer Ressource. Verwenden Sie beim Erstellen von Abstraktionen höherer Ebene (AEM-Seiten, Assets, Tags usw.) die vom entsprechenden Manager bereitgestellten Methoden.
resourceResolver.delete(resource);
resourceResolver.commit();