Use-API Java HTL

L'API Java Use-API du langage de modèle HTML (HTML Template Language) permet à un fichier HTML d'accéder aux méthodes d'assistance dans une classe Java personnalisée par data-sly-use. Cela permet à l’ensemble de la logique métier complexe d’être encapsulée dans le code Java, tandis que le code HTL traite uniquement la production directe des balises.

Un objet Java Use-API peut être un POJO simple, instancié par une implémentation particulière via le constructeur par défaut du POJO.

Les POJO Use-API peuvent également exposer une méthode publique, appelée init, avec la signature suivante :

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

La carte bindings peut contenir des objets qui fournissent un contexte au script HTML actuellement exécuté que l’objet Use-API peut utiliser pour son traitement.

Un exemple simple

Nous allons commencer avec un composant HTL qui n’a pas de classe d’utilisation. Il se compose d’un seul fichier, /apps/my-example/components/info.html

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

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

Nous avons également ajouté du contenu pour que le rendu de ce composant s’effectue à l’adresse /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."
}

Lorsque ce contenu est consulté, le fichier HTL est exécuté. Dans le code HTML, nous utilisons l'objet contextuel properties pour accéder aux ressources title et description actuelles et les afficher. Le code HTML de sortie sera :

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

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

Ajout d’une classe d’utilisation

Le composant info en tant que tel n’a pas besoin d’une classe d’utilisation pour remplir sa fonction (très simple). Cependant, dans certains cas, vous devez effectuer des opérations qui ne peuvent pas être effectuées dans HTL, et vous avez donc besoin d’une classe d’utilisation. Cependant, gardez à l’esprit les considérations suivantes :

NOTE

Une classe d’utilisation doit être utilisée uniquement lorsqu’une opération ne peut pas être effectuée uniquement dans HTL.

Par exemple, supposons que vous souhaitiez que le composant info affiche les propriétés title et description de la ressource, mais entièrement en minuscules. Comme HTL ne possède pas de méthode pour passer des chaînes en minuscules, vous aurez besoin d’une classe d’utilisation. Nous pouvons effectuer cela en ajoutant une classe d’utilisation Java et en modifiant le fichier info.html comme suit :

/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;
    }
}

Dans les sections suivantes, nous examinons les différentes parties du code.

Classe Java locale / Bundle

La classe d’utilisation Java peut être installée de deux manières : en local ou groupée. Cet exemple utilise une installation locale.

Dans le cas d’une installation locale, le fichier source Java est placé à côté du fichier HTL, dans le même dossier de référentiel. Sur demande, la source est automatiquement compilée. Aucune étape de compilation ou d’assemblage distincte n’est requise.

Dans le cas d’une installation groupée, la classe Java doit être compilée et déployée au sein d’un groupe OSGi par le biais du mécanisme de déploiement groupé standard d’AEM (voir Classe Java groupée).

NOTE

Une classe d’utilisation Java locale est recommandée quand la classe d’utilisation est spécifique au composant en question.

Une classe d’utilisation Java groupée est recommandée quand le code Java met en œuvre un service qui est utilisé par plusieurs composants HTL.

Le package Java est le chemin du référentiel

Lorsque vous utilisez une installation locale, le nom du package de la classe d’utilisation doit correspondre à l’emplacement du dossier de référentiel, avec tous les tirets dans le chemin remplacés par des traits de soulignement dans le nom du package.

Dans ce cas, Info.java se trouve à l’adresse /apps/my-example/components/info. Par conséquent, le package est 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 {

   ...

}
NOTE

Il est recommandé d’utiliser des tirets dans les noms des éléments du référentiel lors du développement AEM. Toutefois, les tirets sont des caractères interdits dans les noms de package Java. C’est pourquoi tous les tirets dans le chemin d’accès au référentiel doivent être convertis en traits de soulignement dans le nom du package.

Extension WCMUsePojo

Même s’il existe plusieurs façons d’incorporer une classe Java avec HTL (voir les alternatives à WCMUsePojo), le plus simple est d’étendre la classe WCMUsePojo :

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

package apps.my_example.components.info;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo

    ...
}

Initialisation de la classe

Lorsque la classe d'utilisation est étendue à partir de WCMUsePojo, l'initialisation est effectuée en remplaçant la méthode activate :

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

...

}

Contexte

En règle générale, la méthode activate est utilisé pour précalculer et stocker (dans les variables de membres) les valeurs nécessaires dans votre code HTL, en fonction du contexte actif (la requête et la ressource actives, par exemple).

La classe WCMUsePojo permet d’accéder au même ensemble d’objets de contexte qui est disponible dans un fichier HTL (voir Objets globaux).

Dans une classe qui étend WCMUsePojo, les objets de contexte peuvent être consultés par nom en utilisant

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

Vous pouvez également accéder directement aux objets de contexte fréquemment utilisés par la méthode pratique appropriée :

PageManager getPageManager()
Page getCurrentPage()
Page getResourcePage()
ValueMap getPageProperties()
ValueMap getProperties()
Designer getDesigner()
Conception getCurrentDesign()
Style getCurrentStyle()
Composant getComponent()
ValueMap getInheritedProperties()
Resource getResource()
ResourceResolver getResourceResolver()
SlingHttpServletRequest getRequest()
SlingHttpServletResponse getResponse()
SlingScriptHelper getSlingScriptHelper()

Méthodes getter

Une fois que la classe d’utilisation est initialisée, le fichier HTL est exécuté. Au cours de cette étape, HTL récupère généralement l’état de plusieurs variables de membres de la classe de l’utilisation et les restitue pour la présentation.

Pour permettre l’accès à ces valeurs à partir du fichier HTL, vous devez définir des méthodes getter personnalisées dans la classe d’utilisation, en respectant la convention de dénomination suivante :

  • Une méthode de formulaire getXyz exposera une propriété d’objet appelée xyz dans le fichier HTL.

Dans l’exemple suivant, les méthodes getTitle et getDescription ont pour effet que les propriétés d’objet title et description deviennent accessibles dans le contexte du fichier HTL :

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

...

public class Info extends WCMUsePojo {

    ...

    public String getLowerCaseTitle() {
        return lowerCaseTitle;
    }

    public String getLowerCaseDescription() {
        return lowerCaseDescription;
    }
}

attribut data-sly-use

L’attribut data-sly-use est utilisé pour initialiser la classe d’utilisation dans votre code HTL. Dans notre exemple, l’attribut data-sly-use indique que nous voulons utiliser la classe Info. Nous pouvons utiliser seulement le nom local de la classe, parce que nous utilisons une installation locale (le fichier source Java ayant été placé dans le même dossier que le fichier HTL). Si nous utilisions une installation groupée, nous devrions spécifier le nom de classe complet.

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

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

Identifiant local

L'identifiant info (après le point dans data-sly-use.info) est utilisé dans le fichier HTL pour identifier la classe. La portée de cet identificateur est globale dans le fichier, une fois qu’il a été déclaré. Elle ne se limite pas à l’élément qui contient l’instruction data-sly-use.

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

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

Obtention des propriétés

L’identificateur info est alors utilisé pour accéder aux propriétés de l’objet title et description, qui ont été exposées par les méthodes getter Info.getTitle et Info.getDescription.

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

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

Sortie

Désormais, lorsque nous accédons à /content/my-example.html, elle renvoie le code HTML suivant :

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

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

Au-delà des concepts de base

Dans cette section, nous présenterons d'autres fonctionnalités qui vont au-delà de l'exemple simple ci-dessus :

  • Transmission de paramètres à une classe d’utilisation
  • Classe d’utilisation Java groupée
  • Alternatives à WCMUsePojo

Transmission des paramètres

Les paramètres peuvent être transmis à un chemin de classe d’utilisation lors de l’initialisation. Par exemple, nous pouvons effectuer ce qui suit :

/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>

Dans ce cas, nous transmettons un paramètre appelé text. La classe d’utilisation met la chaîne que nous récupérons en majuscules et affiche le résultat avec info.upperCaseText. Voici la classe d’utilisation ajustée :

/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;
    }

    ...
}

Le paramètre est accessible via la méthode WCMUsePojo <T> T get(String paramName, Class<T> type)

Dans notre cas, l’instruction:

get("text", String.class)

La chaîne est alors inversée et exposée par le biais de la méthode:

getReverseText()

Ne transmettez des paramètres que depuis data-sly-template

Bien que l’exemple ci-dessus soit techniquement correct, il n’est pas très judicieux de transmettre une valeur depuis HTL pour initialiser une classe d’utilisation, lorsque la valeur en question est disponible dans le contexte d’exécution du code HTL (ou, trivialement, la valeur est statique, comme ci-dessus).

C’est parce que la classe d’utilisation aura toujours accès au même contexte d’exécution que le code HTL. Cela entraîne une ’importation recommandée :

NOTE

La transmission d’un paramètre à une classe d’utilisation ne doit être effectuée que lorsque cette dernière est utilisée dans un fichier data-sly-template, qui est lui-même appelé à partir d’un autre fichier HTL avec les paramètres qui doivent être transmis.

Par exemple, créons un fichier data-sly-template distinct avec notre exemple existant. Nous appellerons le nouveau fichier extra.html. Il contient un bloc data-sly-template appelé 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>

Le modèle extra prend un seul paramètre, text. Il initialise ensuite la classe d’utilisation Java ExtraHelper extraHelper avec le nom local et lui transmet la valeur du paramètre de modèle text en tant que paramètre de classe d’utilisation text.

Le corps du modèle récupère la propriété extraHelper.reversedText (qui, de son côté, appelle en fait ExtraHelper.getReversedText()) et affiche cette valeur.

Nous adaptons également notre fichier info.html existant pour utiliser ce nouveau modèle :

/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>

Le fichier info.html contient désormais deux instructions data-sly-use, celle d’origine qui importe la classe d’utilisation Java Info, et une nouvelle qui importe le fichier de modèle sous le nom local extra.

Notez que nous aurions pu placer le bloc de modèle à l’intérieur du fichier info.html pour éviter le deuxième data-sly-use, mais un fichier de modèle distinct est plus courant et plus facile à réutiliser.

La classe Info est utilisée comme avant, en appelant ses méthodes getter getLowerCaseTitle() et getLowerCaseDescription() via leurs propriétés HTL correspondantes info.lowerCaseTitle et info.lowerCaseDescription.

Nous effectuons ensuite un data-sly-call sur le modèle extra et lui transmettons la valeur properties.description en tant que paramètre text.

La classe d’utilisation Java Info.java est modifiée pour gérer le nouveau paramètre de texte :

/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;
    }
}

Le paramètre text est récupéré avec get("text", String.class), et la valeur est inversée et rendue disponible en tant qu’objet HTL reversedText par le getter getReversedText().

Classe Java groupée

Avec une classe d’utilisation groupée, la classe doit être compilée, mise en package et déployée dans AEM à l’aide du mécanisme de déploiement standard de regroupement OSGi. Contrairement à installation locale, la classe d’utilisation de déclaration du package doit être appelé normalement :

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

package org.example.app.components;

import com.adobe.cq.sightly.WCMUsePojo;

public class Info extends WCMUsePojo {
    ...
}

et l’instruction data-sly-use doit référencer le nom de classe entièrement qualifié, et non pas simplement le nom de 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>

Alternatives à WCMUsePojo

La méthode la plus fréquente pour créer une classe d’utilisation Java consiste à étendre WCMUsePojo. Cependant, il existe un certain nombre d’options supplémentaires. Pour comprendre ces variantes, il est utile de comprendre comment l’instruction HTL data-sly-use s’exécute en arrière-plan.

Supposons que vous ayez l’instruction data-sly-use suivante :

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

Le système traite l’instruction comme suit :

(1)

  • Si un fichier local UseClass.java existe dans le même répertoire que le fichier HTL, essayez de compiler et de charger cette classe. En cas de réussite, accédez à la partie (2).
  • Autrement, interprétez UseClass comme nom de classe entièrement qualifié et essayez de le charger à partir de l’environnement OSGi. En cas de réussite, accédez à la partie (2).
  • Autrement, interprétez UseClass en tant que chemin d’accès vers un fichier HTL ou JavaScript et chargez ce fichier. En cas de réussite, accédez à la partie (4).

(2)

  • Essayez d'adapter le Resource actuel à UseClass. En cas de réussite, passez à (3).
  • Autrement, essayez d’adapter la Request active à UseClass. En cas de réussite, accédez à la partie (3).
  • Autrement, essayez d’instancier UseClass avec un constructeur sans argument. En cas de réussite, accédez à la partie (3).

(3)

  • Dans HTL, liez l’objet nouvellement adapté ou créé au nom localName.
  • Si UseClass implémente io.sightly.java.api.Use, appelez la méthode init, en transmettant le contexte d'exécution actuel (sous la forme d'un objet javax.scripting.Bindings).

(4)

  • Si UseClass est un chemin d’accès à un fichier HTL contenant un data-sly-template, préparez le modèle.
  • Autrement, si UseClass est un chemin d’accès à une classe d’utilisation JavaScript, préparez la classe d’utilisation (voir Use-API JavaScript).

Voici quelques points importants à propos de la description ci-dessus :

  • Toute classe qui est adaptable à partir de Resource ou de Request, ou qui dispose d’un constructeur sans argument peut être une classe d’utilisation. La classe ne doit pas nécessairement étendre WCMUsePojo ni même mettre en œuvre Use.
  • Cependant, si la classe d’utilisation met vraiment en œuvreUse, sa méthode init est automatiquement appelée avec le contexte actif, ce qui vous permet d’y placer le code d’initialisation qui dépend de ce contexte.
  • Une classe d’utilisation qui étend WCMUsePojo est seulement un cas spécial de mise en œuvre de Use. Elle fournit les méthodes de contexte pratiques et sa méthode activate est appelée automatiquement à partir de Use.init.

Mise en œuvre directe de l’utilisation de l’interface

Bien que la méthode la plus simple pour créer une classe d’utilisation consiste à étendre WCMUsePojo, il est également possible de mettre en œuvre directement l’interface io.sightly.java.api.Useelle même.

L’interfaceUse ne définit qu’une seule méthode :

public void init(javax.script.Bindings bindings)

La méthode init sera appelée lors de l’initialisation de la classe avec un objet Bindings qui contient tous les objets de contexte et tous les paramètres transmis dans la classe d’utilisation.

Toutes les fonctionnalités supplémentaires (telles que l’équivalent de WCMUsePojo.getProperties()) doivent être implémentées explicitement à l’aide de l’objet javax.script.Bindings. Par exemple :

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");
    }
    ...
}

La principale raison pour laquelle vous devriez mettre en œuvre l’interface Use par vous-même au lieu d’étendre WCMUsePojo est si vous souhaitez utiliser une sous-classe d’une classe existante en tant que classe d’utilisation.

Adaptable à partir de la ressource

Une autre option consiste à utiliser une classe d’assistance qui est adaptable à partir de org.apache.sling.api.resource.Resource.

Supposons que vous ayez besoin d’écrire un script HTL qui affiche le mimetype d’une ressource DAM. Dans ce cas, vous savez que quand votre script HTL est appelé, ce sera dans le contexte d’une Resource qui enveloppe un Node JCR de type dam:Asset.

Vous savez qu’un nœud dam:Asset possède le type de structure suivant :

Structure du référentiel

{
  "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": {
                  ...
              }
            }
          }  
        }
      }
    }
  }
}

Nous montrons ici la ressource (une image JPEG) qui est fournie avec une installation par défaut d’AEM en tant qu’exemple de projet geometrixx. La ressource est appelée jane_doe.jpg et son mimetype est image/jpeg.

Pour accéder à la ressource à partir de HTL, vous pouvez déclarer com.day.cq.dam.api.Asset comme classe dans l'instruction data-sly-use, puis utiliser une méthode get de Asset pour récupérer les informations souhaitées. Par exemple :

mimetype.html

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

L’instruction data-sly-use demande à HTL d’adapter la Resource active à un Asset et de lui donner le nom local asset. Il appelle ensuite la méthode getMimeType de Asset à l’aide du raccourci getter HTL : asset.mimeType.

Adaptable à partir de la requête

Il est également possible d'utiliser comme classe d'utilisation toute classe adaptable à partir de org.apache.sling.api.SlingHttpServletRequest

Comme pour le cas ci-dessus d'une variable adaptable use-class de Resource, une variable use-class adaptable de SlingHttpServletRequest peut être spécifiée dans l'instruction data-sly-use. Lors de l’exécution, la requête active sera adaptée à la classe donnée et l’objet produit sera rendu disponible dans HTL.

Sur cette page