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:

  1. AEM
  2. Sling
  3. JCR
  4. 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:

  1. com.adobe.cq
  2. com.adobe.granite
  3. 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:

CAUTION
Die AEM QueryBuilder-API führt dazu, dass ein ResourceResolver-Objekt nicht ordnungsgemäß geschlossen wird. Um dieses Problem zu vermeiden, folgen Sie diesem Code-Beispiel.

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

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.

CAUTION
Eine umfassende Verwendung der Sitzungs- und Knotenschnittstellen von JCR-APIs in einer AEM-Anwendung ist für den Code schädlich. Achten Sie darauf, stattdessen Sling-APIs zu verwenden.

Häufige Anwendungsbereiche von JCR-APIs

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

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über com.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).

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

Sling-Filterregistrierung sling-filter-registration

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();
recommendation-more-help
c92bdb17-1e49-4e76-bcdd-89e4f85f45e6