Bonnes pratiques relatives aux API Java™
Adobe Experience Manager (AEM) repose sur une riche pile logicielle open source qui expose de nombreuses API Java™ à utiliser pendant le développement. Cet article couvre les principales API et explique quand et pourquoi elles doivent être utilisées.
AEM repose sur quatre ensembles principaux d’API Java™.
-
Adobe Experience Manager (AEM)
- Abstractions de produits telles que des pages, des ressources, des workflows, etc.
-
Framework web Apache Sling
- REST et abstractions basées sur des ressources telles que des ressources, des cartes de valeurs et des requêtes HTTP.
-
JCR (Apache Jackrabbit Oak)
- Abstractions de données et de contenu telles que des nœuds, propriétés et sessions.
-
OSGi (Apache Felix)
- Abstractions du conteneur d’application OSGi telles que les services et les composants (OSGi).
Règle de base de préférence d’API Java™
La règle générale est de préférer les API/abstractions dans l’ordre suivant :
- AEM
- Sling
- JCR
- OSGi
Si une API est fournie par AEM, préférez-la à Sling, JCR et OSGi. Si AEM ne fournit pas d’API, préférez Sling à JCR et OSGi.
Cet ordre est une règle générale, ce qui signifie qu’il existe des exceptions. Les raisons possibles de dérogation à cette règle sont les suivantes :
-
Exceptions connues, comme décrit ci-dessous.
-
La fonctionnalité requise n’est pas disponible dans une API de niveau supérieur.
-
Le fonctionnement dans le contexte du code existant (code de produit personnalisé ou AEM) qui utilise lui-même une API située plus bas dans la liste de préférence, et le coût de déplacement vers la nouvelle API est injustifiable.
- Il est préférable d’utiliser systématiquement l’API de niveau inférieur plutôt que de créer un mélange.
API d’AEM
Les API d’AEM fournissent des abstractions et des fonctionnalités spécifiques aux cas d’utilisation productifs.
Par exemple, les API AEM PageManager et Page fournissent des abstractions pour les nœuds cq:Page
dans AEM représentant les pages web.
Alors que ces nœuds sont disponibles via les API Sling en tant que ressources et via les API JCR en tant que nœuds, les API AEM fournissent des abstractions pour les cas d’utilisation courants. L’utilisation des API d’AEM permet d’assurer un comportement cohérent entre AEM, le produit, ainsi que les personnalisations et les extensions d’AEM.
com.adobe.* et com.day.* API
Les API d’AEM ont une préférence intra-package, identifiée par les packages Java™ suivants, par ordre de préférence :
com.adobe.cq
com.adobe.granite
com.day.cq
Le package com.adobe.cq
prend en charge les cas d’utilisation de produits, alors que com.adobe.granite
prend en charge les cas d’utilisation de plateformes inter-produits, tels que les workflows et les tâches (qui sont utilisés dans plusieurs produits : AEM Assets, Sites, etc.).
Le package com.day.cq
contient des API « originales ». Ces API traitent des abstractions et fonctionnalités de base qui existaient avant et/ou autour de l’acquisition par Adobe de Day CQ. Ces API sont prises en charge et doivent être évitées, sauf si les packages com.adobe.cq
ou com.adobe.granite
ne fournissent PAS d’alternative (plus récente).
De nouvelles abstractions telles que Content Fragments et Experience Fragments sont conçues dans l’espace com.adobe.cq
plutôt que celui com.day.cq
décrit ci-dessous.
API de requête
AEM prend en charge plusieurs langages de requête. Les trois langages principaux sont : JCR-SQL2, XPath et AEM Query Builder.
La préoccupation la plus importante est de conserver un langage de requête cohérent dans l’ensemble de la base de code, afin de réduire la complexité et les coûts de compréhension.
Tous les langages de requête possèdent effectivement les mêmes profils de performance, comme Apache Oak les transpile vers JCR-SQL2 pour l’exécution de la requête finale, et le temps de conversion vers JCR-SQL2 est négligeable par rapport au temps de requête lui-même.
L’API préférée est AEM Query Builder, qui constitue le niveau d’abstraction le plus élevé et fournit une API robuste pour la création, l’exécution et la récupération des résultats des requêtes, et fournit les éléments suivants :
-
Une construction de requêtes simple et paramétrée (paramètres de requête modélisés en tant que carte).
-
Des prédicats AEM prenant en charge des exigences de requête courantes.
-
Une API extensible, permettant le développement de prédicats de requête personnalisés.
-
JCR-SQL2 et XPath peuvent être exécutés directement via Sling et les API JCR, renvoyant des résultats de Sling ressources ou de nœuds JCR, respectivement.
API Sling
Apache Sling est le framework web RESTful qui sous-tend AEM. Sling fournit le routage des requêtes HTTP, modélise les nœuds JCR en tant que ressources, fournit un contexte de sécurité, etc.
Les API Sling ont l’avantage supplémentaire d’être créées pour l’extension, ce qui signifie qu’il est souvent plus facile et plus sûr d’augmenter le comportement des applications créées à l’aide des API Sling plutôt que des API JCR moins extensibles.
Utilisations courantes des API Sling
-
Accéder aux nœuds JCR en tant que Sling Resources et accéder à leurs données via ValueMaps.
-
Fournir un contexte de sécurité via le ResourceResolver.
-
Créer et supprimer des ressources via les méthodes créer/déplacer/copier/supprimer de ResourceResolver.
-
Mettre à jour des propriétés via ModifiableValueMap.
-
Créer des blocs de création de traitement des demandes.
-
Blocs de création de traitement de travail asynchrone.
API JCR
Les API JCR (Java™ Content Repository) 2.0 font partie d’une spécification pour les implémentations JCR (dans le cas d’AEM, Apache Jackrabbit Oak). Toute implémentation JCR doit se conformer à ces API et les mettre en œuvre. Il s’agit donc de l’API de niveau le plus bas pour interagir avec le contenu AEM.
Le JCR lui-même est un magasin de données NoSQL hiérarchique/basé sur une arborescence qu’AEM utilise comme référentiel de contenu. Le JCR dispose d’une vaste gamme d’API prises en charge, allant des opérations CRUD de contenu à l’interrogation de contenu. Malgré la robustesse de ces API, il est rare qu’elles soient privilégiées par rapport aux abstractions Sling et AEM de niveau supérieur.
Privilégiez toujours les API JCR plutôt que les API Apache Jackrabbit Oak. Les API JCR sont destinées à l’interaction avec un référentiel JCR, tandis que les API Oak sont destinées à l’implémentation d’un référentiel JCR.
Erreurs courantes à propos des API JCR
Bien que JCR soit le référentiel de contenu d’AEM, ses API ne constituent PAS la méthode privilégiée pour interagir avec le contenu. Privilégiez plutôt les API AEM (Page, Ressources, Balise, etc.) ou les API de ressource Sling, car elles fournissent de meilleures abstractions.
Utilisations courantes des API JCR
-
Observation JCR (écoute des événements JCR)
-
Créer des structures de nœuds profondes
API OSGi
Les API OSGi et les API de niveau supérieur (AEM, Sling, et JCR) se chevauchent peu. Il est rare d’avoir à utiliser les API OSGi et cela nécessite un haut niveau d’expertise en développement AEM.
API OSGi vs Apache Felix
OSGi définit une spécification que tous les conteneurs OSGi doivent implémenter et respecter. L’implémentation OSGi d’AEM, Apache Felix, fournit également plusieurs de ses propres API.
- Privilégiez les API OSGi (
org.osgi
) par rapport aux API Apache Felix (org.apache.felix
).
Utilisations courantes des API OSGi
-
Annotations OSGi pour la déclaration des services et composants OSGi.
- Privilégiez les annotations OSGi Declarative Services (DS) 1.2 par rapport aux annotations SCR Felix pour la déclaration des services et composants OSGi.
-
API OSGi pour l’annulation/l’enregistrement dynamique dans le code des services/composants OSGi.
- Privilégiez l’utilisation des annotations OSGi DS 1.2 lorsque la gestion conditionnelle des services/composants OSGi n’est pas nécessaire (ce qui est le plus souvent le cas).
Exceptions à la règle
Vous trouverez ci-dessous des exceptions courantes aux règles définies ci-dessus.
API OSGi
Lorsque vous traitez des abstractions OSGi de bas niveau, telles que la définition ou la lecture dans les propriétés de composant OSGi, les abstractions les plus récentes fournies par org.osgi
sont privilégiées par rapport aux abstractions Sling de niveau supérieur. Les abstractions Sling concurrentes n’ont pas été marquées comme @Deprecated
et suggèrent l’alternative org.osgi
.
Notez également que la définition de nœud de configuration OSGi préfère le format cfg.json
au format sling:OsgiConfig
.
API Assets AEM
-
Privilégiez
com.day.cq.dam.api
par rapport àcom.adobe.granite.asset.api
.- Les API Assets
com.day.cq
fournissent des outils plus complets pour le cas d’utilisation de la gestion des ressources AEM. - Les API Assets Granite prennent en charge la gestion des ressources de bas niveau (version, relations).
- Les API Assets
API de requête
- AEM QueryBuilder ne prend pas en charge certaines fonctions de requête, telles que les suggestions, la vérification orthographique et les indices d’index parmi d’autres fonctions moins courantes. Il est préférable d’effectuer des requêtes avec ces fonctions à l’aide de JCR-SQL2.
Enregistrement de servlet Sling sling-servlet-registration
- Pour l’enregistrement du servlet Sling, privilégiez les annotations OSGi DS 1.2 avec @SlingServletResourceTypes par rapport à
@SlingServlet
.
Enregistrement de filtre Sling sling-filter-registration
- Pour l’enregistrement de filtre Sling, privilégiez les annotations OSGi DS 1.2 avec @SlingServletFilter par rapport à
@SlingFilter
Extraits de code utiles
Vous trouverez ci-dessous des extraits de code Java™ qui illustrent les bonnes pratiques relatives aux cas d’utilisation courants des API mentionnées. Ces extraits illustrent également comment passer d’API au niveau de préférence inférieur à des API dont le niveau de préférence est supérieur.
Session JCR à ResourceResolver Sling
Fermer automatiquement ResourceResolver Sling
Depuis AEM 6.2, le ResourceResolver Sling est AutoClosable
dans une instruction try-with-resources. Avec cette syntaxe, un appel explicite à resourceResolver .close()
n’est pas nécessaire.
@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) { .. }
Fermer manuellement ResourceResolver Sling
ResourceResolver peut être fermé manuellement dans un bloc finally
, si la technique de fermeture automatique illustrée ci-dessus ne peut pas être utilisée.
@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(); }
}
Chemin JCR vers Resource Sling
Resource resource = ResourceResolver.getResource("/path/to/the/resource");
Nœud JCR vers Resource Sling
Resource resource = resourceResolver.getResource(node.getPath());
Resource Sling vers AEM Assets
Approche recommandée
La fonction DamUtil.resolveToAsset(..)
résout n’importe quelle ressource sous dam:Asset
vers l’objet Asset en montant l’arborescence selon les besoins.
Asset asset = DamUtil.resolveToAsset(resource);
Approche alternative
L’adaptation d’une ressource à un objet Asset nécessite que la ressource elle-même soit le nœud dam:Asset
.
Asset asset = resource.adaptTo(Asset.class);
Ressource Sling vers la page AEM
Approche recommandée
pageManager.getContainingPage(..)
résout toute ressource sous cq:Page
vers l’objet Page en montant l’arborescence selon les besoins.
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");
Approche alternative alternative-approach-1
L’adaptation d’une ressource à un objet Page requiert que la ressource elle-même soit le nœud cq:Page
.
Page page = resource.adaptTo(Page.class);
Lire les propriétés de page AEM
Utilisez les getters de l’objet Page pour obtenir des propriétés connues (getTitle()
, getDescription()
, etc.) et page.getProperties()
pour obtenir la ValueMap [cq:Page]/jcr:content
pour récupérer d’autres propriétés.
Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());
Lire des propriétés de métadonnées Asset AEM
L’API Asset fournit des méthodes pratiques pour lire les propriétés à partir du nœud [dam:Asset]/jcr:content/metadata
. Il ne s’agit pas d’une ValueMap, le deuxième paramètre (valeur par défaut et conversion de type automatique) n’est pas pris en charge.
Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");
Lire des propriétés Resource Sling read-sling-resource-properties
Lorsque les propriétés sont stockées dans des emplacements (propriétés ou ressources relatives) auxquels les API AEM (Page, Asset) ne peuvent pas accéder directement, les ressources Sling et les ValueMaps peuvent être utilisées pour obtenir les données.
ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");
Dans ce cas, l’objet AEM peut devoir être converti en Resource Sling pour localiser efficacement la propriété ou la sous-ressource souhaitée.
Page AEM vers Resource Sling
Resource resource = page.adaptTo(Resource.class);
Asset AEM vers Resource Sling
Resource resource = asset.adaptTo(Resource.class);
Écrire des propriétés à l’aide de la ModifiableValueMap de Sling
Utiliser la ModifiableValueMap de Sling pour écrire des propriétés sur des nœuds. Cela permet uniquement d’écrire sur le nœud immédiat (les chemins de propriété relatifs ne sont pas pris en charge).
Notez que l’appel à .adaptTo(ModifiableValueMap.class)
nécessite des autorisations d’écriture sur la ressource, sinon elle renvoie null.
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");
resource.getResourceResolver().commit();
Créer une page AEM
Utilisez toujours PageManager pour créer des pages, car un modèle de page est nécessaire pour définir et initialiser correctement les pages dans 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(); }
Créer une ressource Sling
ResourceResolver prend en charge les opérations de base pour la création de ressources. Lors de la création d’abstractions de niveau supérieur (Pages, Ressources, Balises AEM, etc.), utilisez les méthodes fournies par leurs gestionnaires respectifs.
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();
Supprimer une ressource Sling
ResourceResolver prend en charge la suppression d’une ressource. Lors de la création d’abstractions de niveau supérieur (Pages, Ressources, Balises AEM, etc.), utilisez les méthodes fournies par leurs gestionnaires respectifs.
resourceResolver.delete(resource);
resourceResolver.commit();