Comprender las prácticas recomendadas de la API de Java

Adobe Experience Manager (AEM) se basa en una rica pila de software de código abierto que expone muchas API de Java para su uso durante el desarrollo. Este artículo explora las API principales y cuándo y por qué deben usarse.

AEM se basa en 4 conjuntos principales de API de Java.

  • Adobe Experience Manager (AEM)

    • abstracciones de productos, como páginas, recursos, flujos de trabajo, etc.
  • Apache SlingMarco web

    • REST y abstracciones basadas en recursos, como recursos, mapas de valores y solicitudes HTTP.
  • JCR (Apache Jackrabbit Oak)

    • Abstracciones de datos y contenido, como nodos, propiedades y sesiones.
  • OSGi (Apache Felix)

    • abstracciones de contenedores de aplicaciones OSGi como servicios y componentes (OSGi).

Preferencia de la API de Java "regla general"

La regla general es preferir las API/abstracciones en el siguiente orden:

  1. AEM
  2. Sling
  3. JCR
  4. los paquetes

Si AEM proporciona una API, preferirla a Sling, JCR y OSGi. Si AEM no proporciona una API, prefiera Sling antes que JCR y OSGi.

Este orden es una regla general, lo que significa que existen excepciones. Los motivos aceptables para romper con esta regla son:

  • Excepciones bien conocidas, como se describe a continuación.

  • La funcionalidad requerida no está disponible en una API de nivel superior.

  • Operar en el contexto de código existente (código de producto personalizado o de AEM) que utiliza una API menos preferida y el coste de pasar a la nueva API es injustificable.

    • Es mejor utilizar la API de nivel inferior de forma consistente que crear una mezcla.

API de AEM

Las API de AEM proporcionan abstracciones y funciones específicas de los casos de uso productizados.

Por ejemplo, las API PageManager y Page de AEM proporcionan abstracciones para los nodos cq:Page en AEM que representan páginas web.

Aunque estos nodos están disponibles a través de las API Sling como recursos y las API de JCR como nodos, las API de AEM proporcionan abstracciones para casos de uso comunes. El uso de las API de AEM garantiza un comportamiento coherente entre el producto AEM y las personalizaciones y extensiones de AEM.

com.adobe.* vs. com.day.* API

Las API de AEM tienen una preferencia dentro del paquete, identificada por los siguientes paquetes Java, en orden de preferencia:

  1. com.adobe.cq
  2. com.adobe.granite
  3. com.day.cq

com.adobe.cq admite casos de uso de productos, mientras que com.adobe.granite admite casos de uso de plataformas entre productos, como flujos de trabajo o tareas (que se utilizan en todos los productos: AEM Assets, Sites, etc.).

com.day.cq contiene API "originales". Estas API tratan de abstracciones y funcionalidades básicas que existían antes o alrededor de la adquisición de [!DNL Day CQ] por parte de Adobe. Estas API son compatibles y no deben evitarse, a menos que com.adobe.cq o com.adobe.granite proporcionen una alternativa (más reciente).

Las nuevas abstracciones como Content Fragments y Experience Fragments se crean en el espacio com.adobe.cq en lugar de com.day.cq que se describe a continuación.

API de consulta

AEM admite varios idiomas de consulta. Los 3 idiomas principales son JCR-SQL2, XPath y AEM Query Builder.

La preocupación más importante es mantener un lenguaje de consulta consistente en toda la base de código, para reducir la complejidad y el coste de comprensión.

Todos los lenguajes de consulta tienen efectivamente los mismos perfiles de rendimiento, ya que Apache Oak los transpila a JCR-SQL2 para la ejecución de la consulta final, y el tiempo de conversión a JCR-SQL2 es insignificante comparado con el tiempo de consulta en sí.

La API preferida es AEM Query Builder, que es la abstracción de más alto nivel y proporciona una API sólida para construir, ejecutar y recuperar resultados para consultas, y proporciona lo siguiente:

ATENCIÓN

La API de QueryBuilder de AEM filtra un objeto ResourceResolver. Para mitigar esta fuga, siga esta muestra de código.

Sling API

Sling Se adjunta al marco web de RESTful que sustenta AEM. Sling proporciona enrutamiento de solicitud HTTP, modela nodos JCR como recursos, proporciona contexto de seguridad y mucho más.

Sling Las API tienen la ventaja añadida de ser creadas para la extensión, lo que significa que a menudo es más fácil y seguro aumentar el comportamiento de las aplicaciones creadas mediante Sling API que las API de JCR menos ampliables.

Uso común de las API Sling

API de JCR

Las API JCR (Repositorio de contenido Java) 2.0 forman parte de una especificación para implementaciones JCR (en el caso de AEM, Apache Jackrabbit Oak). Toda la implementación de JCR debe cumplir e implementar estas API y, por lo tanto, es la API de menor nivel para interactuar con el contenido de AEM.

El propio JCR es un almacén de datos NoSQL basado en árboles y jerárquico que AEM utiliza como repositorio de contenido. El JCR tiene una amplia gama de API compatibles, que van desde el CRUD de contenido hasta la consulta de contenido. A pesar de esta API robusta, es raro que sean preferidos sobre las abstracciones de AEM y Sling de nivel superior.

Siempre prefiera las API de JCR en lugar de las API de Apache Jackrabbit Oak. Las API de JCR están destinadas a interactuar con un repositorio JCR, mientras que las API de Oak están destinadas a implementar un repositorio JCR.

Conceptos erróneos comunes sobre las API de JCR

Aunque el JCR es el repositorio de contenido de AEM, sus API NO son el método preferido para interactuar con el contenido. En su lugar, prefiera las API de AEM (página, recursos, etiqueta, etc.) o las API de recursos de Sling, ya que proporcionan mejores abstracciones.

ATENCIÓN

El uso generalizado de las interfaces de nodo y sesión de las API de JCR en una aplicación de AEM es el olor del código. Asegúrese de que las API Sling no deben usarse en su lugar.

Uso común de las API de JCR

API de OSGi

Hay poca superposición entre las API de OSGi y las API de nivel superior (AEM, Sling y JCR), y la necesidad de utilizar las API de OSGi es escasa y requiere un alto nivel de experiencia en desarrollo de AEM.

OSGi frente a las API de Apache Felix

OSGi define una especificación a la que todos los contenedores OSGi deben implementarse y ajustarse. La implementación OSGi de AEM, Apache Felix, también proporciona varias de sus propias API.

  • Prefiera las API de OSGi (org.osgi) sobre las API de Apache Felix (org.apache.felix).

Uso común de las API de OSGi

Excepciones a la regla

Las siguientes son excepciones comunes a las reglas definidas anteriormente.

API de AEM Asset

  • Prefiera com.day.cq.dam.api sobre com.adobe.granite.asset.api.

    • Mientras que las API de com.day.cq recursos proporcionan herramientas más gratuitas para los casos prácticos de administración de recursos de AEM.
    • Las API de Granite Assets admiten casos de uso de administración de recursos de bajo nivel (versión, relaciones).

API de consulta

  • AEM QueryBuilder no admite ciertas funciones de consulta, como sugerencias, la revisión de ortografía y las sugerencias de índice, entre otras funciones menos comunes. Para consultar con estas funciones se prefiere JCR-SQL2.

Sling Registro de servlet

  • Sling registro de servlet, preferir anotaciones OSGi DS 1.2 con @ SlingServletResourceTyesover @SlingServlet

Sling Registro de filtros

  • Sling filtro de registro, preferir anotaciones OSGi DS 1.2 con @ SlingServletFilterover @SlingFilter

Fragmentos de código útiles

Los siguientes son fragmentos útiles de código Java que ilustran las prácticas recomendadas para casos de uso comunes usando API discutidas. Estos fragmentos también ilustran cómo pasar de las API preferidas a las más preferidas.

Sesión JCR a Sling ResourceResolver

Cerrar automáticamente ResourceResolver de Sling

Desde AEM 6.2, el Sling ResourceResolver es AutoClosable en una instrucción try-with-resources. Con esta sintaxis, no es necesaria una llamada explícita 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) { .. }

ResourceResolver de Sling cerrado manualmente

ResourceResolvers se puede cerrar manualmente en un bloque finally si no se puede utilizar la técnica de cierre automático que se muestra arriba.

@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(); }
}

Ruta JCR a Sling Resource

Resource resource = ResourceResolver.getResource("/path/to/the/resource");

Nodo JCR a Sling Resource

Resource resource = resourceResolver.getResource(node.getPath());

Sling Resource a AEM Asset

Enfoque recomendado

DamUtil.resolveToAsset(..)resuelve cualquier recurso debajo del objeto Asset dam:Asset al subir por el árbol según sea necesario.

Asset asset = DamUtil.resolveToAsset(resource);

Enfoque alternativo

La adaptación de un recurso a un recurso requiere que el propio recurso sea el nodo dam:Asset.

Asset asset = resource.adaptTo(Asset.class);

Sling Recurso a la página de AEM

Enfoque recomendado

pageManager.getContainingPage(..) resuelve cualquier recurso que se encuentra debajo del objeto Página cq:Page al subir por el árbol según sea necesario.

PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");

Enfoque alternativo

La adaptación de un recurso a una página requiere que el propio recurso sea el nodo cq:Page.

Page page = resource.adaptTo(Page.class);

Leer las propiedades de página de AEM

Utilice los captadores del objeto Página para obtener propiedades conocidas (getTitle(), getDescription(), etc.) y page.getProperties() para obtener el [cq:Page]/jcr:content ValueMap para recuperar otras propiedades.

Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());

Leer las propiedades de metadatos de AEM Asset

La API de Asset proporciona métodos prácticos para leer propiedades del nodo [dam:Asset]/jcr:content/metadata. Tenga en cuenta que no se trata de un ValueMap, no se admite el segundo parámetro (valor predeterminado y conversión de tipo automático).

Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");

Leer Sling Resource propiedades

Cuando las propiedades se almacenan en ubicaciones (propiedades o recursos relativos) en las que las API de AEM (Página, Recurso) no pueden acceder directamente, se pueden utilizar los Sling Recursos y ValueMaps para obtener los datos.

ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");

En este caso, es posible que el objeto AEM tenga que convertirse en un Sling Resource para localizar de forma eficaz la propiedad o el subrecurso que desee.

Página AEM a Sling Resource

Resource resource = page.adaptTo(Resource.class);

AEM Asset a Sling Resource

Resource resource = asset.adaptTo(Resource.class);

Escribir propiedades utilizando el mapa de valor modificado de Sling

Utilice Sling ModitableValueMap para escribir propiedades en los nodos. Esto solo puede escribir en el nodo inmediato (no se admiten las rutas de propiedad relativas).

Tenga en cuenta que la llamada a .adaptTo(ModifiableValueMap.class) requiere permisos de escritura para el recurso; de lo contrario, devolverá un valor nulo.

ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);

properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");

resource.getResourceResolver().commit();

Crear una página de AEM

Utilice siempre PageManager para crear páginas, ya que se necesita una plantilla de página, para definir e inicializar correctamente las páginas en 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(); }

Crear un Sling recurso

ResourceResolver admite operaciones básicas para crear recursos. Al crear abstracciones de nivel superior (páginas de AEM, recursos, etiquetas, etc.) utilice los métodos proporcionados por sus respectivos administradores.

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();

Eliminar un Sling recurso

ResourceResolver admite la eliminación de un recurso. Al crear abstracciones de nivel superior (páginas de AEM, recursos, etiquetas, etc.) utilice los métodos proporcionados por sus respectivos administradores.

resourceResolver.delete(resource);

resourceResolver.commit();

En esta página