Práticas recomendadas da API Java™

O Adobe Experience Manager (AEM) é construído em uma pilha avançada de software de código aberto que expõe muitas APIs Java™ para uso durante o desenvolvimento. Este artigo explora as principais APIs e quando e por que elas devem ser usadas.

O AEM é construído em quatro conjuntos principais de APIs Java™.

  • Adobe Experience Manager (AEM)

    • Abstrações de produto, como páginas, ativos, fluxos de trabalho etc.
  • Apache Sling Web Framework

    • REST e abstrações baseadas em recursos, como recursos, mapas de valores e solicitações HTTP.
  • JCR (Apache Jackrabbit Oak)

    • Dados e abstrações de conteúdo, como nó, propriedades e sessões.
  • OSGi (Apache Felix)

    • Abstrações do contêiner de aplicativo OSGi, como serviços e componentes (OSGi).

Preferência da API Java™ "regra geral"

A regra geral é preferir as APIs/abstrações na seguinte ordem:

  1. AEM
  2. Sling
  3. JCR
  4. OSGi

Se uma API for fornecida pelo AEM, prefira ela a Sling, JCR e OSGi. Se o AEM não fornecer uma API, prefira Sling a JCR e OSGi.

Essa ordem é uma regra geral, o que significa que existem exceções. Os motivos aceitáveis para romper com essa regra são:

  • Exceções bem conhecidas, conforme descrito abaixo.

  • A funcionalidade necessária não está disponível em uma API de nível superior.

  • Operar no contexto de código existente (código de produto personalizado ou AEM) que usa uma API menos preferencial, e o custo de mover para a nova API é injustificável.

    • É melhor usar de forma consistente a API de nível inferior do que criar uma combinação.

APIs AEM

As APIs do AEM fornecem abstrações e funcionalidades específicas para casos de uso produzidos.

Por exemplo, as APIs de PageManager e Page do AEM fornecem abstrações para nós cq:Page no AEM que representam páginas da Web.

Embora esses nós estejam disponíveis por meio de Sling APIs como Recursos e JCR APIs como Nós, as APIs AEM fornecem abstrações para casos de uso comuns. Usar as APIs do AEM garante um comportamento consistente entre o AEM e as personalizações e extensões do AEM.

com.adobe.* vs. com.day.* APIs

As APIs AEM têm uma preferência dentro do pacote, identificada pelos seguintes pacotes Java™, em ordem de preferência:

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

O pacote com.adobe.cq oferece suporte a casos de uso de produtos, enquanto o com.adobe.granite oferece suporte a casos de uso de plataformas entre produtos, como fluxo de trabalho ou tarefas (que são usadas em produtos: AEM Assets, Sites e assim por diante).

O pacote com.day.cq contém APIs "originais". Essas APIs abordam abstrações e funcionalidades principais que existiam antes e/ou em torno da aquisição de Day CQ pelo Adobe. Essas APIs são suportadas e devem ser evitadas, a menos que os pacotes com.adobe.cq ou com.adobe.granite NÃO forneçam uma alternativa (mais recente).

Novas abstrações, como Content Fragments e Experience Fragments, são criadas no espaço com.adobe.cq em vez de com.day.cq, conforme descrito abaixo.

APIs de consulta

O AEM oferece suporte a vários idiomas de consulta. Os três idiomas principais são JCR-SQL2, XPath e Construtor de Consultas do AEM.

A preocupação mais importante é manter uma linguagem de consulta consistente em toda a base de código, para reduzir a complexidade e os custos de compreensão.

Todas as linguagens de consulta têm efetivamente os mesmos perfis de desempenho, já que Apache Oak as compila para JCR-SQL2 para execução de consulta final, e o tempo de conversão para JCR-SQL2 é insignificante em comparação ao próprio tempo de consulta.

A API preferida é o Construtor de Consultas AEM, que é a abstração de mais alto nível e fornece uma API robusta para construção, execução e recuperação de resultados de consultas, além de fornecer o seguinte:

CAUTION
A API QueryBuilder do AEM vaza um objeto ResourceResolver. Para atenuar esse vazamento, siga esta amostra de código.

Sling APIs

Apache Sling é a estrutura da Web RESTful subjacente ao AEM. Sling fornece roteamento de solicitações HTTP, modela nós JCR como recursos, fornece contexto de segurança e muito mais.

Sling APIs têm a vantagem adicional de serem compiladas para extensão, o que significa que geralmente é mais fácil e seguro aumentar o comportamento de aplicativos compilados usando APIs Sling do que as APIs JCR menos extensíveis.

Usos comuns de Sling APIs

APIs JCR

As APIs do JCR (Java™ Content Repository) 2.0 fazem parte de uma especificação das implementações do JCR (no caso do AEM, Apache Jackrabbit Oak). Toda implementação do JCR deve estar em conformidade com e implementar essas APIs, e, portanto, é a API de nível mais baixo para interagir com o conteúdo do AEM.

O próprio JCR é um armazenamento de dados NoSQL hierárquico/em árvore que o AEM usa como seu repositório de conteúdo. O JCR tem uma grande variedade de APIs compatíveis, que vão desde a CRUD de conteúdo até a consulta de conteúdo. Apesar dessa API robusta, é raro que eles sejam preferidos em relação ao AEM de nível mais alto e Sling abstrações.

Sempre prefira as APIs JCR às APIs do Apache Jackrabbit Oak. As APIs JCR são para interagir com um repositório JCR, enquanto as APIs do Oak são para implementar um repositório JCR.

Equívocos comuns sobre APIs JCR

Embora o JCR seja o repositório de conteúdo do AEM, suas APIs NÃO são o método preferido para interagir com o conteúdo. Em vez disso, prefira as APIs de AEM (Página, Assets, Tag e assim por diante) ou as APIs de recurso do Sling, pois fornecem melhores abstrações.

CAUTION
O uso amplo das interfaces de nó e sessão das APIs JCR em um aplicativo AEM cheira a código. Certifique-se de que as APIs Sling sejam usadas em seu lugar.

Usos comuns de APIs JCR

APIs OSGi

Há pouca sobreposição entre as APIs OSGi e as APIs de nível superior (AEM, Sling e JCR), e a necessidade de usar APIs OSGi é rara e requer um alto nível de conhecimento em desenvolvimento de AEM.

OSGi versus APIs Apache Felix

O OSGi define uma especificação que todos os contêineres OSGi devem implementar e estar em conformidade. A implementação OSGi do AEM, Apache Felix, também fornece várias de suas próprias APIs.

  • Preferir APIs OSGi (org.osgi) a APIs Apache Felix (org.apache.felix).

Usos comuns de APIs OSGi

Exceções à regra

Veja a seguir exceções comuns às regras definidas acima.

APIs OSGi

Ao lidar com abstrações OSGi de baixo nível, como definir ou ler nas propriedades do componente OSGi, as abstrações mais recentes fornecidas por org.osgi são preferíveis em relação às abstrações Sling de nível superior. As abstrações Sling concorrentes não foram marcadas como @Deprecated e sugerem a alternativa org.osgi.

Observe também que a definição do nó de configuração OSGi prefere cfg.json ao formato sling:OsgiConfig.

APIs de ativos AEM

  • Preferir com.day.cq.dam.api a com.adobe.granite.asset.api.

    • Embora as APIs do Assets com.day.cq forneçam ferramentas mais complementares para os casos de uso de gerenciamento de ativos do AEM.
    • As APIs do Granite Assets são compatíveis com casos de uso de gerenciamento de ativos de baixo nível (versão, relações).

APIs de consulta

  • O Construtor de Consultas AEM não oferece suporte a determinadas funções de consulta, como sugestões, verificação ortográfica e dicas de índice, entre outras funções menos comuns. É preferível consultar com essas funções o JCR-SQL2.

Registro do Servlet Sling sling-servlet-registration

Registro de filtro Sling sling-filter-registration

Trechos de código úteis

A seguir estão trechos de código Java™ úteis que ilustram as práticas recomendadas para casos de uso comuns usando APIs discutidas. Esses fragmentos também ilustram como mover de APIs menos preferenciais para APIs mais preferenciais.

Sessão JCR para Sling ResourceResolver

Fechamento automático do Sling ResourceResolver

Desde o AEM 6.2, o ResourceResolver Sling é AutoClosable em uma instrução try-with-resources. Usando esta sintaxe, uma chamada explícita para resourceResolver .close() não é necessária.

@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) { .. }

Sling ResourceResolver fechado manualmente

ResourceResolvers pode ser fechado manualmente em um bloco finally, se a técnica de fechamento automático mostrada acima não puder ser usada.

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

Caminho JCR para Sling Resource

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

Nó JCR para Sling Resource

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

Sling Resource para AEM Asset

Método recomendado

A função DamUtil.resolveToAsset(..) resolve qualquer recurso sob dam:Asset para o objeto de Ativo, subindo na árvore, conforme necessário.

Asset asset = DamUtil.resolveToAsset(resource);

Abordagem alternativa

Adaptar um recurso a um Ativo requer que o próprio recurso seja o nó dam:Asset.

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

Página de Sling recurso para AEM

Método recomendado

pageManager.getContainingPage(..) resolve qualquer recurso sob cq:Page para o objeto Página, subindo na árvore, conforme necessário.

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

Abordagem alternativa alternative-approach-1

Adaptar um recurso a uma Página requer que o próprio recurso seja o nó cq:Page.

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

Ler propriedades da página AEM

Use os getters do objeto Page para obter propriedades conhecidas (getTitle(), getDescription() e assim por diante) e page.getProperties() para obter o ValueMap [cq:Page]/jcr:content para recuperar outras propriedades.

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

Ler propriedades de metadados de ativos do AEM

A API de Ativo fornece métodos convenientes para a leitura de propriedades do nó [dam:Asset]/jcr:content/metadata. Este não é um ValueMap, o segundo parâmetro (valor padrão e conversão de tipo automático) não é compatível.

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

Ler propriedades de Sling Resource read-sling-resource-properties

Quando as propriedades são armazenadas em locais (propriedades ou recursos relativos) onde as APIs do AEM (Página, Ativo) não podem acessar diretamente, os Recursos do Sling e os Mapas de Valores podem ser usados para obter os dados.

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

Nesse caso, o objeto AEM pode ter que ser convertido em um Sling Resource para localizar com eficiência a propriedade ou o sub-recurso desejado.

Página AEM para Sling Resource

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

Ativo AEM para Sling Resource

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

Gravar propriedades usando ModisibleValueMap de Sling

Use ModisibleValueMap de Sling para gravar propriedades em nós. Isso só pode gravar no nó imediato (caminhos de propriedade relativos não são compatíveis).

Observe que a chamada para .adaptTo(ModifiableValueMap.class) requer permissões de gravação para o recurso, caso contrário, ela retornará um valor nulo.

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

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

resource.getResourceResolver().commit();

Criar uma página AEM

Sempre use o PageManager para criar páginas, pois ele precisa de um Modelo de página, é necessário para definir e inicializar páginas corretamente no 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(); }

Criar um recurso Sling

O ResourceResolver dá suporte a operações básicas para a criação de recursos. Ao criar abstrações de nível superior (Páginas AEM, Assets, Tags e assim por diante), use os métodos fornecidos pelos respectivos Gerentes.

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

Excluir um recurso Sling

ResourceResolver dá suporte à remoção de um recurso. Ao criar abstrações de nível superior (Páginas AEM, Assets, Tags e assim por diante), use os métodos fornecidos pelos respectivos Gerentes.

resourceResolver.delete(resource);

resourceResolver.commit();
recommendation-more-help
c92bdb17-1e49-4e76-bcdd-89e4f85f45e6