Article summary

Summary

Discusses how to develop an AEM 6.4 HTML Template Language (HTL) component that uses the WCMUsePojo class and invokes a third-party Restful web service. Also, discusses how to use the GSON Java library within AEM to parse the JSON response.

A special thank you to Ratna Kumar Kotla, a top AEM community member, for testing this article to ensure it works.

Digital Marketing Solution(s) Adobe Experience Manager 6.4
Audience Developer
Required Skills Java, HTML, JavaScript
Version 6.4

Note:

You can download an AEM package that contains the code used in this article. Download the package and deploy using package manager. The purpose of this code is to show the community these concepts in action. That is, it's to illustrate how to write a HTL component that invokes a third-party Restful web service. This community code is for teaching purposes only and not meant to go into production as is.

You can view the sample community application by using the following URL:

http://localhost:4502/editor.html/content/restHTL64/en.html (assuming you deploy on author).

Download

Introduction

You can create an Adobe Experience Manager 6.4 HTML Template Language (HTL) component that displays data retrieved from a third-party Restful web service. For example, assume you want to display a country name based on its 2 character alphanumeric ISO code. In this situation, you can develop an Experience Manager HTL component that displays this data, as shown in this illustration.

Client
A HTL 6.4 that returns data from a RESTFUL web service call

In this use case, the HTL component contains a class that extends WCMUsePojo. This HTL uses a Java class that extends com.adobe.cq.sightly.WCMUsePojo.

This class uses Java application logic to send a HTTP Request to a third-party Restful web service. The web service returns the following data in JSON format. The HTL component uses the Java library GSON to parse the JSON data and then displays the data in the client so it appears in the AEM web page.

{
"RestResponse":{
"messages":[
"Country found matching code [US]."
],
"result":{
"name":"United States of America",
"alpha2_code":"US",
"alpha3_code":"USA"
}
}
}

This development article steps you through how to build an Experience Manager 6.4 HTL component by using an AEM Maven Archetype 13 project. 

Note:

This article uses the free Restful web service call that can be seen here: http://www.groupkt.com/post/c9b0ccb9/country-and-other-related-rest-webservices.htm

Create an AEM Maven 13 archetype project

Create an Experience Manager archetype 13 project by using the Maven archetype plugin. In this example, assume that the working directory is C:\AdobeCQ.

M10
Files generated by Maven 10 Archetype

To create an Experience Manager archetype project, perform these steps:

1. Open the command prompt and go to your working directory (for example, C:\AdobeCQ).

2. Run the following Maven command:

mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=13 -DarchetypeCatalog=https://repo.adobe.com/nexus/content/groups/public/

3. When prompted, specify the following information:

  • groupId - restHTL64
  • artifactId - restHTL64
  • version - 1.0-SNAPSHOT
  • package - com.aem
  • appsFolderName - restHTL64
  • artifactName - restHTL64
  • componentGroupName - restHTL64
  • contentFolderName - restHTL64
  • cssId - restHTL64
  • packageGroup - restHTL64
  • siteName - restHTL64

4. WHen prompted, specify Y.

5. Once done, you will see a message like:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:42 min
[INFO] Finished at: 2016-04-25T14:34:19-04:00
[INFO] Final Memory: 16M/463M
[INFO] ------------------------------------------------------------------------

6. Change the working directory to restHTL64 and then enter the following command.

mvn eclipse:eclipse

After you run this command, you can import the project into Eclipse as discussed in the next section.

Add Java files to the Maven project using Eclipse

To make it easier to work with the Maven generated project, import it into the Eclipse development environment, as shown in the following illustration.

Project2
Eclipse Import Project Dialog

The Eclipse project that you work in to build the OSGi bundle that uses HTL API is restHTL64.core. You do not have to work in the other projects under restHTL64.

Note:

Do not worry about the errors reported in Eclipse. It does not read the POM file where the APIs are resolved. You build the bundle with Maven. Eclipse is used to edit the Java files and the POM file.

The next step is to add Java files to the com.aem.core package. The Java classes are named HeroRestComponent and HeroTextBean. The HeroTextBean is a Java bean that has class members to store data returned from the web service. In this article, it has these string class members:

  • private String fullcountry ;
  • private String codecountry ; 
  •  

For example, the fullcountry data member stores the country name that is returned by the Restful web service call.

The HeroRestComponent class is the Java side of the HTL component and extends WCMUsePojo, which is an abstract class that implements the Use interface. A HTL component's Java class implements this abstract class. For information, see WCMUsePojo.

HeroTextBean class

The HeroTextBean defines data members and contains getter and setter methods. The following Java code represents this class.

package com.aem.core;

/* Stores data returned from the Restful web service */
public class HeroTextBean {
     
     
    private String fullcountry ; 
    private String codecountry ; 
    
     
    public void setCountry(String fullcountry)
    {
        this.fullcountry = fullcountry ; 
    }
     
    public String getCountry()
    {
        return this.fullcountry ; 
    }
     
        
    public void setCode(String codecountry)
    {
        this.codecountry = codecountry ; 
    }
     
    public String getCode()
    {
        return this.codecountry ; 
    }
     
    
  
     
 
}

HeroRestComponent class

The HeroRestComponent is the Java server-side part of the AEM HTL component. This class extends the WCMUsePojo class. You override the activate method in this class.

Notice the method named getJSON. This method uses the Java HTTP API to invoke a third party Restful web service. 

The Restful web service returns the name of the country that is based on the ISO code. The code value is set by an author using a component dialog. As specified earily in this article, the data is returned within JSON format.

To parse the JSON data, the Java library GSON is used to parse the data. For information about this Java library, see https://github.com/google/gson.

Web service data values are stored by calling the heroTextBean setter method. For example:

heroTextBean.setCountry(myCountry);

This is how a web service is invoked, JSON values are parsed (using GSON) and stored in the heroTextBean instance. The following Java code represents the HeroRestComponent Java class.

 

package com.aem.core;

import com.adobe.cq.sightly.WCMUsePojo;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import com.day.cq.tagging.Tag;
import com.day.cq.tagging.TagManager;
    
   
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
     
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
  
import javax.jcr.Node;
import javax.jcr.Session;
     
  
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
   
import com.google.gson.*;   
import org.apache.http.client.methods.HttpGet;  
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
  
import java.io.StringReader ;
import java.io.StringWriter;
  
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
  
    
public class HeroRestComponent
extends WCMUsePojo
{
    
     /** The hero text bean that stores values returned by the RestFul Web Service. */
    private HeroTextBean heroTextBean = null;
         
    /** Default log. */
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
                     
      
    @Override
    public void activate() throws Exception {
  
    
        Node currentNode = getResource().adaptTo(Node.class);
            
        heroTextBean = new HeroTextBean();
           
                    
        //Get the values that the author entered into the AEM dialog and to pass to RESTFul call
        String code = getProperties().get("code", "");
       
        String myJSON = getJSON(code);
        
        String myCountry=parse(myJSON);
            
      
        //Set Bean to store the values
        heroTextBean.setCountry(myCountry);
        heroTextBean.setCode(code);
                      
    }
         
         
         
    public HeroTextBean getHeroTextBean() {
        return this.heroTextBean;
    }
       
    
    //Parse the JSON to get back the Country name
    public static String parse(String jsonLine) {
        JsonElement jelement = new JsonParser().parse(jsonLine);
        JsonObject jobject = jelement.getAsJsonObject();
        jobject = jobject.getAsJsonObject("RestResponse");
        jobject = jobject.getAsJsonObject("result");
        String result = jobject.get("name").getAsString();
        return result;
    }       
  //Invokes a third party Restful Web Service and returns the results in a JSON String
    private String getJSON(String code)
    {

        try
        {

          
            DefaultHttpClient httpClient = new DefaultHttpClient();

            HttpGet getRequest = new HttpGet("http://services.groupkt.com/country/get/iso2code/"+code);
            getRequest.addHeader("accept", "application/json");


            HttpResponse response = httpClient.execute(getRequest);

            if (response.getStatusLine().getStatusCode() != 200) {
                throw new RuntimeException("Failed : HTTP error code : "
                        + response.getStatusLine().getStatusCode());
            }

            BufferedReader br = new BufferedReader(new InputStreamReader((response.getEntity().getContent())));

            String output;
            String myJSON="" ;
            while ((output = br.readLine()) != null) {
                //System.out.println(output);
                myJSON = myJSON + output;
            }


            httpClient.getConnectionManager().shutdown();
           return myJSON ;
        }

        catch (Exception e)
        {
            e.printStackTrace() ;
        }

        return null;
    }
       
      
}

Note:

The GSON library is exposed by existing Experience Manager OGSi bundle.

Modify the Maven POM file

Add the following two dependencies to the POM file located at C:\AdobeCQ\restHTL64.

<dependency>
    <groupId>com.adobe.aem</groupId>
    <artifactId>uber-jar</artifactId>
    <version>6.4.0</version>
    <classifier>apis</classifier>
    <scope>provided</scope>
</dependency>
               
  <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>

You need to modify two POM files. The first one is the parent POM file shown in this illustration.

MPP
Parent POM

Add the dependency shown above to the Parent POM file. Next, modify the POM file located at C:\AdobeCQ\restHTL64\core. The following code represents this POM file.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 |  Copyright 2017 Adobe Systems Incorporated
 |
 |  Licensed under the Apache License, Version 2.0 (the "License");
 |  you may not use this file except in compliance with the License.
 |  You may obtain a copy of the License at
 |
 |      http://www.apache.org/licenses/LICENSE-2.0
 |
 |  Unless required by applicable law or agreed to in writing, software
 |  distributed under the License is distributed on an "AS IS" BASIS,
 |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  See the License for the specific language governing permissions and
 |  limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>restHTL64</groupId>
        <artifactId>restHTL64</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <artifactId>restHTL64.core</artifactId>
    <packaging>bundle</packaging>
    <name>restHTL64 - Core</name>
    <description>Core bundle for restHTL64</description>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.sling</groupId>
                <artifactId>maven-sling-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- Import any version of javax.inject, to allow running on multiple versions of AEM -->
                        <Import-Package>javax.inject;version=0.0.0,*</Import-Package>
                        <Sling-Model-Packages>
                            com.aem.core
                        </Sling-Model-Packages>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- OSGi Dependencies -->
        <dependency>
    <groupId>com.adobe.aem</groupId>
    <artifactId>uber-jar</artifactId>
    <version>6.4.0</version>
    <classifier>apis</classifier>
    <scope>provided</scope>
</dependency>
               
  <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.0</version>
    </dependency>
 
 
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.cmpn</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.annotation</artifactId>
        </dependency>
        <!-- Other Dependencies -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.jcr</groupId>
            <artifactId>jcr</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
        <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.models.api</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
        </dependency>
        <dependency>
            <groupId>junit-addons</groupId>
            <artifactId>junit-addons</artifactId>
        </dependency>
    </dependencies>
</project>

Build the OSGi bundle using Maven

To build the OSGi bundle by using Maven, perform these steps:

  1. Open the command prompt and go to the C:\AdobeCQ\restHTL64.
  2. Run the following maven command: mvn -PautoInstallPackage install.
  3. The OSGi component can be found in the following folder: C:\AdobeCQ\restHTL64\core\target. The file name of the OSGi component is restHTL64.core-1.0-SNAPSHOT.jar.

The command -PautoInstallPackage automatically deploys the OSGi bundle to Experience Manager.

Create the HTL Front End Component

When you use the Maven 13 archetype to create an Experience Manager project, a default front end project is created, as shown in the following illustration.

 

CRXDELite
Default files created by Adobe Maven 13 Archetype project

Add HTL code

In this article, you are going to use a component that was build by the Maven archetype. The component is created here: 

/apps/restHTL64/components/content/helloworld

Add the following code.

<p>This AEM HTL component searches for a corresponding Country by its 2 character alphanumeric code </p>
<div data-sly-use.heroTextObject="com.aem.core.HeroRestComponent" data-sly-test="${heroTextObject}">

		<h1>AEM 6.4 HTL Restful example</h1>

	   <h3>Full Country: ${heroTextObject.heroTextBean.country}</h3>
       <h3>Code: ${heroTextObject.heroTextBean.code}</h3>

</div>

In this example, notice data-sly-use.heroTextObject references the Java component: com.aem.core.HeroTextComponent.

Next notice these lines of code:

<div data-sly-use.heroTextObject="com.aem.core.HeroRestComponent" data-sly-test="${heroTextObject}">

 <h3>Full Country: ${heroTextObject.heroTextBean.country}</h3>

This is how you interact with the Java server-side part of the component. In this example, you are writing out the value of the heroTextBean object's data member that stores the country name.  Notice that the heroTextBean.country returns the country name. This is how an AEM HTL component can dynamically display data returned by a Restul web service.

Create the component dialog

The following illustrations shows the component's dialog that lets an AEM Author specify a country ISO code.

Dialog
Components dialog

When building a dialog for the Touch UI view, you define the type of control (for example, a text field) by setting the sling:resourceType property. In contrast, when building a dialog for the classic view, you define the type of control by setting its xtype property.

Note:

For information about working with granite/coral resource types, see Building Experience Manager Component using Granite/Coral Resource Types.

Note:

You can install the package at the start of this article instead of manually creating the dialog nodes. 

Perform these tasks to create the AEM Touch UI dialog for the helloworld component:

1. Select /apps/restHTL64/components/content/helloworld.

2. Right click and select Create, Create Node.

3. Enter the following values:

  • Name: cq:dialog
  • Type: nt:unstructured

4. Add the following properties to the cq:dialog node.

  • helppath (String) - en/cq/current/wcm/default_components.html#Carousel
  • jcr:title (String) - Hero Text
  • sling:resourceType (Stgring) - cq/gui/components/authoring/dialog

5. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog.

6. Right click and select Create, Create Node. Enter the following values:

  • Name: content
  • Type: nt:unstructured

7. Add the following property to the content node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

8. Click on the following node: /apps/restHTL63/components/content/helloworld/cq:dialog/content.

9. Right click and select Create, Create Node. Enter the following values:

  • Name: layout
  • Type: nt:unstructured

10. Add the following properties to the layout node.

  • sling:resourceType (String) - granite/ui/components/foundation/layouts/tabs
  • type (String) -nav

11. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content.

12. Right click and select Create, Create Node. Enter the following values:

  • Name: items
  • Type: nt:unstructured

13. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items.

14. Right click and select Create, Create Node. Enter the following values:

  • Name: herotext
  • Type: nt:unstructured

15. Add the following properties to the herotext node (this node represents the tab).

  • jcr:title (String) - Hero Text Properties
  • sling:resourceType (String) - granite/ui/components/foundation/section

16. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext.

17. Right click and select Create, Create Node. Enter the following values:

  • Name: layout
  • Type: nt:unstructured

18. Add the following property to the layout node.

  • sling:resourceType (String) - granite/ui/components/foundation/layouts/fixedcolumns

19. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext.

20. Right click and select Create, Create Node. Enter the following values:

  • Name: items
  • Type: nt:unstructured

21. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext/items.

22. Right click and select Create, Create Node. Enter the following values:

  • Name: columns
  • Type: nt:unstructured

23. Click on the following node: /apps/restHTL/components/content/helloworld/cq:dialog/content/items/herotext/items/column.

24. Add the following property to the columns node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

25. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext/items/column.

26. Right click and select Create, Create Node. Enter the following values:

  • Name: items
  • Type: nt:unstructured

27. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext/items/column/items.

28. Right click and select Create, Create Node. Enter the following values:

  • Name: headingText
  • Type: nt:unstructured

29. Click on the following node: /apps/restHTL64/components/content/helloworld/cq:dialog/content/items/herotext/items/column/items/headingText.

30. Add the following properties to the headingText node (this node represents the Heading Text input control on the dialog. See the illustration at the start of this article.)

  • fieldLabel (String) - ISO Code
  • name (String) - ./code
  • sling:resourceType (String) - granite/ui/components/foundation/form/textfield

View the output of the HTL component

To access the component, enter the following URL:

http://localhost:4502/editor.html/content/restHTL64/en.html

The following illustration shows the HTL component.

Client
The HTL component created in this article

See also

Join the AEM community at: Adobe Experience Manager Community

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  Twitter™ and Facebook posts are not covered under the terms of Creative Commons.

Legal Notices   |   Online Privacy Policy