Article summary

Summary

Discusses how to create an Adobe Experience Manager (AEM) 6.4 HTL component that queries data from the JCR. This article covers the following Experience Manager developer concepts: 

  • how to use the Experience Manager QueryBuilder API to query the JCR
  • how to use the Sling Mapping Service to use a system user 
  • how to use the UBER 6.4 JAR
  • how to use org.osgi.service.component.annotations
  • how to use the resolverFactory.getServiceResourceResolver method
  • how to display JCR data in a tabular format
  • how to use AJAX and handle the result set by using an HTL component

System users cannot be used to log in normally, only by background processes. The admin user is not a system user, so you cannot use the admin user in a service user mapping like this. You have to create a new system user and assign them the appropriate permissions.If you would like to read more of the background on this change, take a look at https://issues.apache.org/jira/browse/SLING-3854.

A Special thank you to Navin Kaushal for testing this Experience League Community Article to ensure it works. 

Note: To run this code on the Publisher server, you must create the System user on the Publisher instance. 

Digital Marketing Solution(s) Adobe Experience Manager
Audience
Developer (intermediate)
Required Skills
Java, JQuery, JCR SQL2, CSS
AEM Versions(s) Adobe Experience Manager 6.4
Video N/A

Download

Introduction

You can create an Adobe Experience Manager 6.4 application that queries data located in the Java Content Repository (JCR). In this article, you use a com.day.cq.search.QueryBuilder instance that belongs to the QueryBuilder API. This API requires that you define search parameters and an optional filter. After you execute the query, the results are stored in a result set. You can display the result set in an Experience Manager web page. For information, see Interface QueryBuilder.

The following illustration shows JCR data displayed (retrieved by using the QueryBuilder API) within an Experience Manager HTL component.

Data
An Experience Manager component displaying JCR data

In this example, employee data is stored in this JCR location: 

/content/employees

Each employee is stored as a separate nt:unstructured node with these String properties: 

  • address
  • age
  • name
  • salary
  • position
  • start date
datae
Employees data stored in the JCR

Note:

The status property stores the value employee and is not displayed within the component. Ensure that you install the Experience Manager package shown at the start of this article. 

Application logic that queries data from the JCR is implemented as an OSGi bundle that is built using Declarative Services (DS) and Maven. DS is used to inject a ResourceResolverFactory instance into the service. The OSGi bundle is a managed component, which means that the OSGi service container creates the ResourceResolverFactory instance.

Create an Experience Manager System User

Create an Experience Manager System User that can access JCR data located at content/employees. The user account must have privileges, as shown in the following illustration.  

Permissions
User permissions to the JCR

To successfully query JCR data, create an Experience Manager System user by performing these tasks. 

1. Open http://localhost:4502/crx/explorer/index.jsp.

2. Login as admin.

3. Click User Administration.

4. Click Create System User. Name the user data (data is used in this article).

5. Set the UserId. 

6. Click Save

7.  Access the user page at http://localhost:4502/useradmin. 

8.  Select the data user.

9. From the right-hand pane, select the Permissions tab. 

10. Expand the content tab and then select the employees row. 

11. Click all the checkboxes that represent the permissions (click the top row in permissions for this example). 

12. Click the Save button located in the top menu bar (located above the Path heading).  

Configure the Sling Mapper Service

The next step is to configure the Apache Sling Service User Mapper service by adding a new entry. Enter the following value:

QUeryJCR64.core:datawrite=data

where:

  • QUeryJCR64.core – is the Bundle-SymbolicName value of the OSGi bundle this is developed in the upcoming sections of this article.
  • datawrite – the name of the sub service (you reference this value in a Java Map object)
  • data – the system user account with data privileges for content/employees. 

The following illustration shows an entry for this service. 

Mapping
An entry in the Apache Sling Service Mapping Service

To create an entry in the Apache Sling Mapper service, perform these tasks:

1. Go to the Apache Sling Mapper service at http://localhost:4502/system/console/configMgr. 

2. Click OSGI, Configurations. 

2. Scroll to an entry named Apache Sling Service User Mapper Service.

3. Enter the value QUeryJCR64.core:datawrite=data.

4. Click Save

Create an Experience Manager Maven 13 archetype project

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

Maven
Default files created by the Maven archetype plugin

Note:

Before you can create a Maven archetype project, you need to setup Maven. For details, see Creating an Adobe Experience Manager 6.4 Project using Adobe Maven Archetype 13.

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 - QUeryJCR64
  • artifactId - QUeryJCR64
  • version - 1.0-SNAPSHOT
  • package - com.aem.cust
  • appsFolderName - QUeryJCR64
  • artifactName - QUeryJCR64
  • componentGroupName - QUeryJCR64
  • contentFolderName - QUeryJCR64
  • cssId - QUeryJCR64
  • packageGroup - QUeryJCR64
  • siteName - QUeryJCR64

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 QUeryJCR64 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.

Project
The Eclipse Import Project dialog

Add the following Java files to the com.aem.cust.core package:

  • A Java class named Employee that stores employee information.
  • A Java interface named EmployeeInter.
  • A Java class named EmployeeImpl that implements the EmployeeInter interface.

Employee class

The following code represents the Employee class that is used to store employee information. 

package com.aem.cust.core;

//Stores employee information
public class Employee {
     
    //Define private class members
    private String name ;
    private String address ;
    private String position; 
    private String age;
    private String date;
    private String salary;
     
     
    public void setName(String name)
    {
        this.name = name; 
    }
     
    public String getName()
    {
        return this.name;
    }
    
    public void setAddress(String address)
    {
        this.address = address; 
    }
    
    public String getAddress()
    {
        return this.address ; 
    }
     
    public void setPosition(String position)
    {
        this.position = position; 
    }
     
    public String getPosition()
    {
        return this.position;
    }
     
     
    public void setAge(String age)
    {
        this.age = age; 
    }
     
    public String getAge()
    {
        return this.age;
    }
     
    public void setDate(String date)
    {
        this.date = date; 
    }
     
    public String getDate()
    {
        return this.date;
    }
    
    public void setSalary(String salary)
    {
        this.salary = salary; 
    }
     
    public String getSalary()
    {
        return this.salary;
    }
 
}

EmployeeInter interface

The following code represents the EmployeeInter interface. This interface contains a method signature named getEmployeeData. The implementation logic for this method is located in the EmployeeImpl class. 

The following Java code represents the EmployeeInter interface. 

package com.aem.cust.core;

public interface EmployeeInter {

	public String getEmployeeDataQB(); 

}

EmployeeImpl class

The EmployeeImpl class uses the following declarative services annoations

  • @Component – defines the class as a component
  • @Reference – injects a service into the component. 

For information about these annotations, see Official OSGi Declarative Services Annotations in AEM.

In this development article, a ResourceResolverFactory instance is injected into the getEmployeeData method. This instance is required to create a Session instance that lets you query the JCR. To inject a ResourceResolverFactory instance, you use the @Reference annotation to define a class member, as shown in the following example.

//Inject a Sling ResourceResolverFactory
@Reference
private ResourceResolverFactory resolverFactory;

Within the getEmployeeData method, you reference the entry that you specified in the Apache Sling Mapper service. You use this to create a Session object, as shown here.

@Component
public class EmployeeImpl implements EmployeeInter {
	
	/** Default log. */
	protected final Logger log = LoggerFactory.getLogger(this.getClass());
	        
	private Session session;
	            
	//Inject a Sling ResourceResolverFactory
	@Reference
	private ResourceResolverFactory resolverFactory;
	
	
	public String getEmployeeDataQB()
	{
		Employee employee = null;
		   
		List<Employee> employList = new ArrayList<Employee>();
		Map<String, Object> param = new HashMap<String, Object>();
		param.put(ResourceResolverFactory.SUBSERVICE, "datawrite");
		ResourceResolver resolver = null;
		  
		try {
		             
		    //Invoke the adaptTo method to create a Session used to create a QueryManager
		    resolver = resolverFactory.getServiceResourceResolver(param);
		    session = resolver.adaptTo(Session.class);

Note:

When you open a JCR session, there is a reference to the JCR repository object. Every session will consume some memory unless the logout() method is called explicitly. If you do not call this call and create lots of sessions, you risk an out-of-memory exception by your JVM, which terminates the CQ instance. A single leaked session isn’t a problem, but if you have hundreds or thousands of leaked sessions, it might turn into a problem. For more information, see CQ development patterns – Sling ResourceResolver and JCR sessions.

The following Java code represents the EmployeeImpl class. For each employee node that is retrieved from the JCR using the QueryBuilder API, a new Employee object is created and stored in an ArrayList. Data from each Employee object is retrieved from the ArrayList and stored in an XML schema by calling the toXML method. The XML schema is converted to a string that is used as the return value for getEmployeeData

package com.aem.cust.core;

//DS Annotations
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; 


import org.w3c.dom.Document;
import org.w3c.dom.Element;
    
    
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
    
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
    
import javax.jcr.Repository; 
import javax.jcr.SimpleCredentials; 
import javax.jcr.Node; 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.HashMap; 
import java.util.Map; 

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

//QUeryBuilder APIs
import com.day.cq.search.QueryBuilder; 
import com.day.cq.search.Query; 
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.result.SearchResult;
import com.day.cq.search.result.Hit; 


//Sling Imports
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceResolver; 
//import org.apache.sling.api.resource.Resource; 


import org.apache.jackrabbit.commons.JcrUtils;
import javax.jcr.Session;
import javax.jcr.Node; 


@Component
public class EmployeeImpl implements EmployeeInter {
	
	/** Default log. */
	protected final Logger log = LoggerFactory.getLogger(this.getClass());
	        
	private Session session;
	
	@Reference
	private QueryBuilder builder;
	            
	//Inject a Sling ResourceResolverFactory
	@Reference
	private ResourceResolverFactory resolverFactory;
	
		   
		public String getEmployeeDataQB()
		{
			List<Employee> employList = new ArrayList<Employee>();
			Map<String, Object> param = new HashMap<String, Object>();
			param.put(ResourceResolverFactory.SUBSERVICE, "datawrite");
			ResourceResolver resolver = null;
			
			Employee employee = null;
			  
			try {
			             
				//Invoke the adaptTo method to create a Session used to create a QueryManager
			    resolver = resolverFactory.getServiceResourceResolver(param);
			    session = resolver.adaptTo(Session.class);
			    
			    //Query employee data using the QueryBuilder API
			    String fulltextSearchTerm = "employee";
                
			    // create query description as hash map (simplest way, same as form post)
			    Map<String, String> map = new HashMap<String, String>();
			 
			 // create query description as hash map (simplest way, same as form post)
			               
			    map.put("path", "/content/employees");
			    map.put("type", "nt:unstructured");
			    map.put("p.limit", "20"); // same as query.setHitsPerPage(20) below
			                    
			    Query query = builder.createQuery(PredicateGroup.create(map), session);
			                      
			    query.setStart(0);
			    query.setHitsPerPage(20);
			              
			    SearchResult result = query.getResult();
			    int hitsPerPage = result.getHits().size();
			    
			    // iterating over the results
			    for (Hit hit : result.getHits()) {
			    	
			    	//For each node-- create an Employee instance
					employee = new Employee();
				    
			    
			    	javax.jcr.Node node = hit.getNode();
			            
			    	 //Set all Employee object fields
					 employee.setName(node.getProperty("name").getString());
					 employee.setAddress(node.getProperty("address").getString());
					 employee.setPosition(node.getProperty("job").getString());
					 employee.setAge(node.getProperty("age").getString());
					 employee.setDate(node.getProperty("start").getString());
					 employee.setSalary(node.getProperty("salary").getString());
					             
					  //Push the Employee Object to the list
					 employList.add(employee);
					  }
					           
					// Log out
					 session.logout();    
					return convertToString(toXml(employList));    
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			return null; 
		}
				        
		//Convert Employee data retrieved from the AEM JCR
		//into an XML schema to pass back to client
		private Document toXml(List<Employee> employeeList) {
		try
		{
		    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		    DocumentBuilder builder = factory.newDocumentBuilder();
		    Document doc = builder.newDocument();
		                    
		    //Start building the XML to pass back to the AEM client
		    Element root = doc.createElement( "Employees" );
		    doc.appendChild( root );
		                 
		    //Get the elements from the collection
		    int custCount = employeeList.size();
		       
		    //Iterate through the collection to build up the DOM           
		     for ( int index=0; index < custCount; index++) {
		    
		         //Get the Employee object from the collection
		         Employee myEmployee = (Employee)employeeList.get(index);
		                         
		         Element Employee = doc.createElement( "Employee" );
		         root.appendChild( Employee );
		                          
		         //Add rest of data as child elements to Employee
		         //Set Name
		         Element name = doc.createElement( "Name" );
		         name.appendChild( doc.createTextNode(myEmployee.getName() ) );
		         Employee.appendChild( name );
		                                                             
		         //Set Address
		         Element address = doc.createElement( "Address" );
		         address.appendChild( doc.createTextNode(myEmployee.getAddress() ) );
		         Employee.appendChild( address );
		                       
		         //Set position
		         Element position = doc.createElement( "Position" );
		         position.appendChild( doc.createTextNode(myEmployee.getPosition() ) );
		         Employee.appendChild( position );
		                      
		         //Set age
		         Element age = doc.createElement( "Age" );
		         age.appendChild( doc.createTextNode(myEmployee.getAge()) );
		         Employee.appendChild( age );
		         
		         //Set Date
		         Element date = doc.createElement( "Date" );
		         date.appendChild( doc.createTextNode(myEmployee.getDate()) );
		         Employee.appendChild( date );
		         
		         //Set sal
		         Element salary = doc.createElement( "Salary" );
		         salary.appendChild( doc.createTextNode(myEmployee.getSalary()) );
		         Employee.appendChild( salary );
		      }
		               
		return doc;
		}
		catch(Exception e)
		{
		    e.printStackTrace();
		    }
		return null;
		}
		    
		    
		private String convertToString(Document xml)
		{
		try {
		   Transformer transformer = TransformerFactory.newInstance().newTransformer();
		  StreamResult result = new StreamResult(new StringWriter());
		  DOMSource source = new DOMSource(xml);
		  transformer.transform(source, result);
		  return result.getWriter().toString();
		} catch(Exception ex) {
		      ex.printStackTrace();
		}
		  return null;
		 }
		   
		 }

SimpleServlet

Modify the SimpleServlet class in the com.aem.cust.core.servlets package. In this example, a @Reference annotation is used to get an EmployeeInter instance. Then the getEmployeeData method is invoked and the servlet returns the XML string that contains the employee data.  

The following Java code represents the SimpleServlet class. 

package com.aem.cust.core.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.api.resource.ValueMap;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import com.aem.cust.core.EmployeeInter ; 


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@Component(service=Servlet.class,
        property={
                Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
                "sling.servlet.methods=" + HttpConstants.METHOD_GET,
                "sling.servlet.paths="+ "/bin/myCustData"
           })
public class SimpleServlet extends SlingAllMethodsServlet {
 
    private static final long serialVersionUid = 1L;
     
    private final Logger logger = LoggerFactory.getLogger(getClass());
     
    @Reference
    private EmployeeInter emplData;
 
    @Override
    protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        
        try
        {
        logger.info("About to call");  
            
        String data=  emplData.getEmployeeDataQB(); 
        logger.info("DATA IS "+data);   
        resp.getWriter().write(data);
     
        }
        catch (Exception e)
        {
            e.printStackTrace(); 
        }
                 
        }
  
}

Modify the Maven POM file

Add the following POM dependency to the POM file located at C:\AdobeCQ\QUeryJCR64.

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

When you add new Java classes under core, you need to modify a POM file to successfully build the OSGi bundle. You modify the POM file located at C:\AdobeCQ\QUeryJCR64\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>QUeryJCR64</groupId>
        <artifactId>QUeryJCR64</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <artifactId>QUeryJCR64.core</artifactId>
    <packaging>bundle</packaging>
    <name>QUeryJCR64 - Core</name>
    <description>Core bundle for QUeryJCR64</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.cust.core
                        </Sling-Model-Packages>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- OSGi Dependencies -->
         <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
     
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-atinject_1.0_spec</artifactId>
        </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\QUeryJCR64.
  2. Run the following maven command: mvn -PautoInstallPackage install.
  3. The OSGi component can be found in the following folder: C:\AdobeCQ\QUeryJCR64\core\target. The file name of the OSGi component is QUeryJCR64.core-1.0-SNAPSHOT.jar.

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

After you deploy the OSGi bundle, you will be able to see it in the Apache Felix Web Console (http://localhost:4502/system/console/configMgr).

OSGi
Apache Felix Web Console Bundles view

Add CSS and JQuery files to a CQ:ClientLibraryFolder node 

When you build the Maven Archetype 13 project, a client lib folder was automatically generated at this JCR location. 

/apps/QUeryJCR64/clientlibs

In this example,  a data grid plugin is used named DataTables. This plugin is used to display the employee data a tabular format (see the illustration shown at the start of this article).

Download the DataTables plugin from the following URL:

http://www.datatables.net/

Download and extract the DataTables archive file. The Experience Manager component uses these files from the DataTables archive file:

  • datatables.min.css
  • datatables.min.js
 

Place these two files into the clientlibs folder, as shown in this illustration.

clientlibs3
The JS and CSS file in a clientlib

Make sure that the clientlib-base has these two properties. 

clientlibs2
Two properties for the clientlibs folder

After you create the Clientlibs folder, add two CSS files, and the JQuery library files, and two map text files.

Note:

Notice that the categories is QUeryJCR64.base. This clientlibs folder is referenced in this file: /apps/QUeryJCR64/components/structure/page/customheaderlibs.html. Notice the following line of code: 

<sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"

     data-sly-call="${clientlib.css @ categories='QUeryJCR64.base'}"/>

Text files

You have to add two text files to the clientlibs folder. These text files map to the JS file and the CSS file. The names of the text files are: css.txt and js.txt

The css.txt file contains the CSS file name: datatables.min.css.

The js.txt file contains the JS file name: datatables.min.js.

Note:

You can develop an easy and fast way to upload files to client lib folders. See the following article for more information: Creating Java Swing applications that posts files to AEM ClientLibs folders

Create the HTL Front End Component

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

CRXDE
Default files created by Adobe Maven 13 Archetype project

Note:

For information about the default files created by the Maven 13 Archetype project, see this community article: Creating an Adobe Experience Manager 6.4 Project using Adobe Maven Archetype 13.

Add HTL code

For the purpose of this article, the HTL code is written within a Maven Archetype 13 default component located here:

/apps/QUeryJCR64/components/content/helloworld

Add the following HTML code to the helloworld.html file. 

<script>


 $(document).ready(function() {
    var table = $('#myTable').DataTable();


    $('#submit').click( function() {




		 //Use JQuery AJAX request to post data to a Sling Servlet
    $.ajax({
         type: 'GET',    
         url:'/bin/myCustData',
         data:'type='+ 'data',
         success: function(msg){

           var xml = msg; 

            var oTable = $('#myTable').dataTable();
            oTable.fnClearTable(true);



             //Loop through this function for each Employee element
                //in the returned XML
                 $(xml).find('Employee').each(function(){
                         
                    var $field = $(this);
                    var Name = $field.find('Name').text();
                    var Job = $field.find('Position').text(); 
                    var Address = $field.find('Address').text();
                    var Age = $field.find('Age').text();
                    var Date = $field.find('Date').text();   
                    var Salary = $field.find('Salary').text();     
 
                    //Set the new data 
                    oTable.fnAddData( [
                        Name,
                        Job,
                        Address,
                        Age,
                        Date,
                        Salary,,]
                    );
            
                    });



            }

            
			});





  });  //end of click

}); // end ready
</script>



 <div>


<table id="myTable" class="display" style="width:100%">
        <thead>
            <tr>
                <th>${properties.text}</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Tiger Nixon</td>
                <td>System Architect</td>
                <td>Edinburgh</td>
                <td>61</td>
                <td>2011/04/25</td>
                <td>$320,800</td>
            </tr>

        </tbody>
        <tfoot>
            <tr>
                <th>${properties.text}</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
            </tr>
            <input type="button" value="Submit"  name="submit" id="submit" value="Get Employees">
        </tfoot>
    </table>

    </div>

In this example, notice an ajax call in made within the script tag:

 

   $.ajax({
         type: 'GET',    
         url:'/bin/myCustData',
         data:'type='+ 'data',
         success: function(msg){

           var xml = msg; 

            var oTable = $('#myTable').dataTable();
            oTable.fnClearTable(true);



             //Loop through this function for each Employee element
                //in the returned XML
                 $(xml).find('Employee').each(function(){
                         
                    var $field = $(this);
                    var Name = $field.find('Name').text();
                    var Job = $field.find('Position').text(); 
                    var Address = $field.find('Address').text();
                    var Age = $field.find('Age').text();
                    var Date = $field.find('Date').text();   
                    var Salary = $field.find('Salary').text();     
 
                    //Set the new data 
                    oTable.fnAddData( [
                        Name,
                        Job,
                        Address,
                        Age,
                        Date,
                        Salary,,]
                    );
            
                    });

            }
            
});

This call is made when the user clicks the Get Employee data button in the HTML table. An AJAX call is made to the simple servlet that you created earlier in this development article. The servlet returns XML data and the code parses the XML and sets the grid with this code: 

  $(xml).find('Employee').each(function(){

                        var $field = $(this);

                    var Name = $field.find('Name').text();

                    var Job = $field.find('Position').text(); 

                    var Address = $field.find('Address').text();

                    var Age = $field.find('Age').text();

                    var Date = $field.find('Date').text();   

                    var Salary = $field.find('Salary').text();     

 

                    //Set the new data 

                    oTable.fnAddData( [

                        Name,

                        Job,

                        Address,

                        Age,

                        Date,

                        Salary,,]

                    );

               });

View the output of the HTL component

To access the component, enter the following URL:

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

Click the Get Employees button. The following video shows this HTL component displaying JCR data.

Displaying JCR Data

Displaying JCR Data
Displaying JCR Data

Note:

In this example, the Name column (the first column) is set by using a component dialog. This article does not focus on building dialogs. If you are interested in knowing more about using different granite resource types in an Experience Manager component dialog, see Building Experience Manager Component using Granite/Coral Resource Types.

Note:

If you do not create a system user and configure the Sling Mapping Service, as described in this article, the grid does not display JCR data. 

See also

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