API de uso do Java do HTL

The HTML Template Language (HTL) Java Use-API enables an HTL file to access helper methods in a custom Java class through data-sly-use. Isso permite que toda a lógica comercial complexa seja encapsulada no código Java, enquanto o código HTL trata somente da produção de marcação direta.

Um objeto Java Use-API pode ser um POJO simples, instanciado por uma implementação específica por meio do construtor padrão do POJO.

Os POJOs Use-API também podem expor um método público, chamado init, com a seguinte assinatura:

    /**
     * Initializes the Use bean.
     *
     * @param bindings All bindings available to the HTL scripts.
     **/
    public void init(javax.script.Bindings bindings);

O bindings mapa pode conter objetos que fornecem contexto para o script HTL executado no momento que o objeto Use-API pode usar para seu processamento.

Um exemplo simples

Vamos start com um componente HTL que não tem uma classe de uso. Consiste num único ficheiro. /apps/my-example/components/info.html

/apps/my-example/component/info/info.html

<div>
    <h1>${properties.title}</h1>
    <p>${properties.description}</p>
</div>

Também adicionamos algum conteúdo para este componente para renderizar em /content/my-example/:

http://<host>:<port>/content/my-example.json

{
    "sling:resourceType": "my-example/component/info",
    "title": "My Example",
    "description": "This Is Some Example Content."
}

Quando esse conteúdo é acessado, o arquivo HTL é executado. No código HTL, usamos o objeto de contexto properties para acessar os recursos atuais title e exibi-los description . O HTML de saída será:

view-source:http://<host>:<port>/content/my-example.html

<div>
    <h1>My Example</h1>
    <p>This Is Some Example Content.</p>
</div>

Adicionar uma classe de uso

O componente info na sua versão atual não precisa de uma classe use para executar sua função (muito simples). Há casos, no entanto, em que você precisa fazer coisas que não podem ser feitas em HTL e, portanto, você precisa de uma classe de uso. Mas lembre-se do seguinte:

Observação

Uma classe use só deve ser usada quando algo não pode ser feito somente em HTL.

Por exemplo, suponha que você deseja que o info componente exiba as title e description propriedades do recurso, mas tudo em minúsculas. Como o HTL não tem um método para sequências de caracteres em minúsculas, você precisará de uma classe de uso. Podemos fazer isso adicionando uma classe de uso Java e alterando a info.html seguinte maneira:

/apps/my-example/component/info/info.html

<div data-sly-use.info="Info">
    <h1>${info.lowerCaseTitle}</h1>
    <p>${info.lowerCaseDescription}</p>
</div>

/apps/my-example/component/info/Info.java

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo {
    private String lowerCaseTitle;
    private String lowerCaseDescription;

    @Override
    public void activate() throws Exception {
        lowerCaseTitle = getProperties().get("title", "").toLowerCase();
        lowerCaseDescription = getProperties().get("description", "").toLowerCase();
    }

    public String getLowerCaseTitle() {
        return lowerCaseTitle;
    }

    public String getLowerCaseDescription() {
        return lowerCaseDescription;
    }
}

Nas seções a seguir, percorremos as diferentes partes do código.

Classe Java Local vs. Pacote

A classe de uso do Java pode ser instalada de duas formas: local ou pacote. Este exemplo usa uma instalação local.

Em uma instalação local, o arquivo de origem Java é colocado junto ao arquivo HTL, na mesma pasta do repositório. A fonte é compilada automaticamente sob demanda. Não é necessária nenhuma etapa separada de compilação ou embalagem.

Em uma instalação de pacote, a classe Java deve ser compilada e implantada em um pacote OSGi usando o mecanismo de implantação de conjunto AEM padrão (consulte Classe Java embutida).

Observação

Uma classe ​local de uso do Java é recomendada quando a classe de uso é específica para o componente em questão.

Uma classe de uso do Java de pacote é recomendada quando o código Java implementa um serviço que é acessado de vários componentes HTL.

O pacote Java é o caminho do repositório

Quando uma instalação local é usada, o nome do pacote da classe use deve corresponder ao do local da pasta do repositório, com todos os hífens no caminho substituídos pelos sublinhados no nome do pacote.

Nesse caso, o pacote Info.java está localizado em /apps/my-example/components/info apps.my_example.components.info:

/apps/my-example/component/info/Info.java

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo {

   ...

}
Observação

Usar hífens nos nomes de itens do repositório é uma prática recomendada no desenvolvimento do AEM. No entanto, os hífens são ilegais nos nomes dos pacotes Java. Por esse motivo, todos os hífens no caminho do repositório devem ser convertidos em sublinhados no nome do pacote.

Extensão WCMUsePojo

Embora haja várias maneiras de incorporar uma classe Java a HTL (consulte Alternativas para WCMUsePojo), o mais simples é estender a WCMUsePojo classe:

/apps/my-example/component/info/Info.java

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo

    ...
}

Inicializando a classe

Quando a classe use é estendida de WCMUsePojo, a inicialização é realizada substituindo o activate método:

/apps/my-example/component/info/Info.java

...

public class Info extends WCMUsePojo {
    private String lowerCaseTitle;
    private String lowerCaseDescription;

    @Override
    public void activate() throws Exception {
        lowerCaseTitle = getProperties().get("title", "").toLowerCase();
        lowerCaseDescription = getProperties().get("description", "").toLowerCase();
    }

...

}

Contexto

Geralmente, o método ativate é usado para pré-calcular e armazenar (em variáveis de membro) os valores necessários no código HTL, com base no contexto atual (a solicitação atual e o recurso, por exemplo).

A WCMUsePojo classe fornece acesso ao mesmo conjunto de objetos de contexto que estão disponíveis em um arquivo HTL (consulte Objetos globais).

Em uma classe que se estende, objetos de contexto podem ser acessados por nome usando WCMUsePojo

<T> T get(String name, Class<T> type)

Como alternativa, os objetos de contexto comumente usados podem ser acessados diretamente pelo método de conveniência apropriado:

PageManager getPageManager()
Página getCurrentPage()
Página getResourcePage()
ValueMap getPageProperties()
ValueMap getProperties()
Designer getDesigner()
Design getCurrentDesign()
Estilo getCurrentStyle()
Componente getComponent()
ValueMap getInheritedProperties()
Recurso getResource()
ResourceResolver getResourceResolver()
SlingHttpServletRequest getRequest()
SlingHttpServletResponse getResponse()
SlingScriptHelper getSlingScriptHelper()

Métodos Getter

Depois que a classe use for inicializada, o arquivo HTL será executado. Durante esse estágio, o HTL normalmente obtém o estado de várias variáveis membro da classe de uso e as renderiza para apresentação.

Para fornecer acesso a esses valores a partir do arquivo HTL, é necessário definir métodos getter personalizados na classe use, de acordo com a seguinte convenção de nomenclatura:

  • Um método do formulário getXyz exporá no arquivo HTL uma propriedade de objeto chamada xyz.

No exemplo a seguir, os métodos getTitle e getDescription resultam nas propriedades do objeto title e description tornam-se acessíveis no contexto do arquivo HTL:

/apps/my-example/component/info/Info.java

...

public class Info extends WCMUsePojo {

    ...

    public String getLowerCaseTitle() {
        return lowerCaseTitle;
    }

    public String getLowerCaseDescription() {
        return lowerCaseDescription;
    }
}

atributo de uso inteligente de dados

O data-sly-use atributo é usado para inicializar a classe use dentro do código HTL. Em nosso exemplo, o data-sly-use atributo declara que queremos usar a classe Info. Podemos usar apenas o nome local da classe, pois estamos usando uma instalação local (uma vez que o arquivo de origem Java foi colocado na mesma pasta do arquivo HTL). Se estivéssemos usando uma instalação de conjunto, teríamos que especificar o nome de classe totalmente qualificado.

/apps/my-example/component/info/info.html

<div data-sly-use.info="Info">
    <h1>${info.lowerCaseTitle}</h1>
    <p>${info.lowerCaseDescription}</p>
</div>

Identificador local

O identificador info (após o ponto em data-sly-use.info) é usado no arquivo HTL para identificar a classe. O escopo desse identificador é global dentro do arquivo, depois que ele é declarado. Não está limitado ao elemento que contém a data-sly-use declaração.

/apps/my-example/component/info/info.html

<div data-sly-use.info="Info">
    <h1>${info.lowerCaseTitle}</h1>
    <p>${info.lowerCaseDescription}</p>
</div>

Obter propriedades

O identificador info é usado para acessar as propriedades do objeto title e description que foram expostas por meio dos métodos getter Info.getTitle e Info.getDescription.

/apps/my-example/component/info/info.html

<div data-sly-use.info="Info">
    <h1>${info.lowerCaseTitle}</h1>
    <p>${info.lowerCaseDescription}</p>
</div>

Saída

Agora, ao acessá-lo, /content/my-example.html ele retornará o seguinte HTML:

view-source:http://<host>:<port>/content/my-example.html

<div>
    <h1>my example</h1>
    <p>this is some example content.</p>
</div>

Além dos fundamentos

Nesta seção, apresentaremos outros recursos que vão além do exemplo simples acima:

  • Passando parâmetros para uma classe de uso.
  • Classe de uso do Java embutida.
  • Alternativas para WCMUsePojo

Passar parâmetros

Os parâmetros podem ser passados para uma classe use na inicialização. Por exemplo, nós poderíamos fazer algo assim:

/content/my-example/component/info/info.html

<div data-sly-use.info="${'Info' @ text='Some text'}">
    <h1>${info.lowerCaseTitle}</h1>
    <p>${info.lowerCaseDescription}</p>
    <p>${info.upperCaseText}</p>
</div>

Aqui estamos passando um parâmetro chamado text. Em seguida, a classe use maiúscula a string que recuperamos e exibe o resultado com info.upperCaseText. Esta é a classe de uso ajustada:

/apps/my-example/component/info/Info.java

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo {

    ...

    private String reverseText;

    @Override
    public void activate() throws Exception {

        ...

        String text = get("text", String.class);
        reverseText = new StringBuffer(text).reverse().toString();

    }

    public String getReverseText() {
        return reverseText;
    }

    ...
}

O parâmetro é acessado pelo WCMUsePojo método <T> T get(String paramName, Class<T> type)

No nosso caso, a declaração:

get("text", String.class)

A string é então revertida e exposta pelo método:

getReverseText()

Enviar apenas parâmetros a partir de modelo de dados

Embora o exemplo acima esteja tecnicamente correto, não faz muito sentido passar um valor de HTL para inicializar uma classe use, quando o valor em questão está disponível no contexto de execução do código HTL (ou, trivialmente, o valor é estático, como acima).

O motivo é que a classe use sempre terá acesso ao mesmo contexto de execução que o código HTL. Isto traz à tona um ponto de importação das melhores práticas:

Observação

A transmissão de um parâmetro para uma classe use só deve ser feita quando a classe use for usada em um data-sly-template arquivo que ela mesma é chamado de outro arquivo HTL com parâmetros que precisam ser transmitidos.

Por exemplo, vamos criar um data-sly-template arquivo separado junto com nosso exemplo existente. Ligaremos para o novo arquivo extra.html. Ele contém um data-sly-template bloco chamado extra:

/apps/my-example/component/info/extra.html

<template data-sly-template.extra="${@ text}"
          data-sly-use.extraHelper="${'ExtraHelper' @ text=text}">
  <p>${extraHelper.reversedText}</p>
</template>

O modelo extra, usa um único parâmetro, text. Em seguida, inicializa a classe de uso do Java ExtraHelper com o nome local extraHelper e passa o valor do parâmetro template text como o parâmetro use-class text.

O corpo do modelo obtém a propriedade extraHelper.reversedText (que, sob o capô, realmente chama ExtraHelper.getReversedText()) e exibe esse valor.

Além disso, adaptamos nosso modelo existente info.html para usar esse novo modelo:

/apps/my-example/component/info/info.html

<div data-sly-use.info="Info"
     data-sly-use.extra="extra.html">

  <h1>${info.lowerCaseTitle}</h1>
  <p>${info.lowerCaseDescription}</p>

  <div data-sly-call="${extra.extra @ text=properties.description}"></div>

</div>

O arquivo info.html agora contém duas data-sly-use declarações, a original que importa a classe de uso do Info Java e uma nova que importa o arquivo de modelo sob o nome local extra.

Observe que podíamos ter colocado o bloco de modelo dentro do info.html arquivo para evitar o segundo data-sly-use, mas um arquivo de modelo separado é mais comum e mais reutilizável.

A Info classe é empregada como antes, chamando seus métodos getter getLowerCaseTitle() e getLowerCaseDescription() por meio de suas propriedades HTL info.lowerCaseTitle e info.lowerCaseDescription.

Em seguida, executamos um teste data-sly-call para o modelo extra e passamos o valor properties.description como parâmetro text.

A classe de uso do Java Info.java foi alterada para lidar com o novo parâmetro de texto:

/apps/my-example/component/info/ExtraHelper.java

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class ExtraHelper extends WCMUsePojo {
    private String reversedText;
    ...

    @Override
    public void activate() throws Exception {
        String text = get("text", String.class);
        reversedText = new StringBuilder(text).reverse().toString();

        ...
    }

    public String getReversedText() {
        return reversedText;
    }
}

O text parâmetro é recuperado com get("text", String.class), o valor é revertido e disponibilizado como o objeto HTL reversedText por meio do getter getReversedText().

Classe Java embutida

Com uma classe de uso de pacote, a classe deve ser compilada, empacotada e implantada no AEM usando o mecanismo de implantação de pacotes OSGi padrão. Em contraste com uma instalação local, a declaração do pacote use-class deve ser chamada normalmente:

/apps/my-example/component/info/Info.java

package org.example.app.components;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo {
    ...
}

e, a data-sly-use declaração deve mencionar o nome da classe totalmente qualificada, em vez de apenas o nome da classe local:

/apps/my-example/component/info/info.html

<div data-sly-use.info="org.example.app.components.info.Info">
  <h1>${info.title}</h1>
  <p>${info.description}</p>
</div>

Alternativas para WCMUsePojo

A maneira mais comum de criar uma classe de uso Java é estender WCMUsePojo. No entanto, existem várias outras opções. Para entender essas variantes, é útil entender como a data-sly-use declaração HTL funciona sob o capô.

Suponha que você tenha a seguinte data-sly-use afirmação:

<div data-sly-use. localName=" UseClass">

O sistema processa a declaração da seguinte maneira:

(1)

  • Se houver um arquivo local UseClass.java no mesmo diretório do arquivo HTL, tente compilar e carregar essa classe. Se tiver êxito, vá para (2).
  • Caso contrário, interprete UseClass como um nome de classe totalmente qualificado e tente carregá-lo do ambiente OSGi. Se tiver êxito, vá para (2).
  • Caso contrário, interprete UseClass como um caminho para um arquivo HTL ou JavaScript e carregue esse arquivo. Se o goto for bem-sucedido (4).

(2)

  • Tente adaptar a corrente Resource à UseClass. Se tiver êxito, vá para (3).
  • Caso contrário, tente adaptar a corrente Request para UseClass. Se tiver êxito, vá para (3).
  • Caso contrário, tente instanciar UseClass com um construtor de argumento zero. Se tiver êxito, vá para (3).

(3)

  • Em HTL, vincule o objeto recém-adaptado ou criado ao nome localName.
  • Se UseClass implementa io.sightly.java.api.Use então chama o init método, transmitindo o contexto de execução atual (na forma de um javax.scripting.Bindings objeto).

(4)

  • Se UseClass for um caminho para um arquivo HTL contendo um data-sly-template, prepare o modelo.
  • Caso contrário, se UseClass for um caminho para uma classe de uso do JavaScript, prepare a classe de uso (consulte JavaScript Use-API).

Alguns pontos importantes sobre a descrição acima:

  • Qualquer classe que seja adaptável, adaptável Resource Requestou que tenha um construtor de argumento zero pode ser uma classe use. A classe não precisa estender WCMUsePojo ou nem mesmo implementar Use.
  • No entanto, se a classe use não implementar Use, seu init método será chamado automaticamente com o contexto atual, permitindo que você coloque o código de inicialização lá que dependa desse contexto.
  • Uma classe de uso que se estende WCMUsePojo é apenas um caso especial de implementação Use. Ele fornece os métodos de contexto de conveniência e seu activate método é chamado automaticamente de Use.init.

Implementação direta do uso da interface

Embora a maneira mais comum de criar uma classe de uso seja estender WCMUsePojo, também é possível implementar diretamente a própria io.sightly.java.api.Use interface.

A Use interface define apenas um método:

public void init(javax.script.Bindings bindings)

O init método será chamado na inicialização da classe com um Bindings objeto que armazena todos os objetos de contexto e quaisquer parâmetros transmitidos para a classe use.

Toda funcionalidade adicional (como o equivalente de WCMUsePojo.getProperties()) deve ser implementada explicitamente usando o javax.script.Bindings objeto. Por exemplo:

Info.java

import io.sightly.java.api.Use;

public class MyComponent implements Use {
   ...
    @Override
    public void init(Bindings bindings) {

        // All standard objects/binding are available
        Resource resource = (Resource)bindings.get("resource");
        ValueMap properties = (ValueMap)bindings.get("properties");
        ...

        // Parameters passed to the use-class are also available
        String param1 = (String) bindings.get("param1");
    }
    ...
}

O caso principal para implementar a Use interface em vez de estender WCMUsePojo é quando você deseja usar uma subclasse de uma classe já existente como a classe use.

Adaptável a partir do recurso

Outra opção é usar uma classe auxiliar adaptável org.apache.sling.api.resource.Resource.

Digamos que você precise gravar um script HTL que exiba o tipo de mimepe de um ativo DAM. Nesse caso, você sabe que quando seu script HTL for chamado, ele estará dentro do contexto de um Resource que envolve um JCR Node com um tipo de nó dam:Asset.

Você sabe que um dam:Asset nó tem uma estrutura como esta:

Estrutura do repositório

{
  "content": {
    "dam": {
      "geometrixx": {
        "portraits": {
          "jane_doe.jpg": {
            ...
          "jcr:content": {
            ...
            "metadata": {
              ...
            },
            "renditions": {
              ...
              "original": {
                ...
                "jcr:content": {
                  "jcr:primaryType": "nt:resource",
                  "jcr:lastModifiedBy": "admin",
                  "jcr:mimeType": "image/jpeg",
                  "jcr:lastModified": "Fri Jun 13 2014 15:27:39 GMT+0200",
                  "jcr:data": ...,
                  "jcr:uuid": "22e3c598-4fa8-4c5d-8b47-8aecfb5de399"
                }
              },
              "cq5dam.thumbnail.319.319.png": {
                  ...
              },
              "cq5dam.thumbnail.48.48.png": {
                  ...
              },
              "cq5dam.thumbnail.140.100.png": {
                  ...
              }
            }
          }  
        }
      }
    }
  }
}

Aqui, mostramos o ativo (uma imagem JPEG) que vem com uma instalação padrão do AEM como parte do exemplo geometrixx do projeto. O ativo é chamado jane_doe.jpg e seu tipo mimético é image/jpeg.

Para acessar o ativo de dentro do HTL, você pode declarar com.day.cq.dam.api.Asset como a classe na instrução data-sly-use e, em seguida, usar um método get de Asset para recuperar as informações desejadas. Por exemplo:

mimetype.html

<div data-sly-use.asset="com.day.cq.dam.api.Asset">
  <p>${asset.mimeType}</p>
</div>

A data-sly-use declaração direciona o HTL para adaptar o atual Resource a um Asset e dar-lhe o nome local asset. Em seguida, ele chama o getMimeType método de Asset uso do comando HTL getter shorthand: asset.mimeType.

Adaptável a partir da solicitação

Também é possível usar como classe de uso qualquer classe que seja adaptável de org.apache.sling.api.SlingHttpServletRequest

Como no caso acima de um adaptador de classe de uso de Resource, um adaptador de classe de uso de SlingHttpServletRequest pode ser especificado na data-sly-use declaração. Após a execução, a solicitação atual será adaptada à classe fornecida e o objeto resultante será disponibilizado dentro de HTL.

Nesta página