Article summary

Summary

This AEM development community article discusses how to develop a tab-based HTML Template Language (HTL- formerly known as Sightly) component that displays AEM DAM digital assets.  Also discusses how to use the Experience Manager Urber 6.2 JAR. 

A special thank you to Ratna Kumar Kotla, a top AEM community member, for contributing AEM code that is used in this article. 

This article covers many AEM concepts that are important to understand as an AEM developer. This article covers the following concepts. 

  • How to create a tab-based HTL component where each tab displays different data.
  • How to use a back-end Java service with the HTL component. 
  • How to display AEM DAM digital assets using HTL.
  • How to use supported HTML5 tags such as the video element to render AEM Assets within a HTL component.
  • How to use a PathField widget in a HTL component dialog.
  • How to use QueryBuilder API within a HTL component
  • How to use a textfield node regex property.
  • How to iterate through a collection in HTL
  • How to use CSS within a HTL component
  • How to invoke the getSlingScriptHelper().getService() to return an instance of a Java service

This article uses an Adobe Maven Archetype project to build an OSGi bundle. If you are not familiar with an Adobe Maven Archetype project, it is recommended that you read the following article: Creating your first AEM Service using an Adobe Maven Archetype project.

Note: HTL is the AEM template language that can be used to replace use of JSP when developing an AEM component. HTL helps you to separate your design from your application logic. For more information, see Introduction to the HTML Template Language.

Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (Advanced)
Required Skills
Java, HTML, CSS
AEM Version(s) AEM 6.2
Video https://youtu.be/LN7qXws39bs

Note:

You can download these AEM packages that contains the Movie component and example digital assets. Install the packages by using AEM package manager. You can access the Movie component (after you install the two packages) by using the following URL: http://localhost:4502/editor.html/content/moviecomponent/en.html.

 

* moviecomponent62-0.0.1-SNAPSHOT.zip
An AEM 6.2 package that contain the HTL movie component

Note:

To read the Experience Manager 6.3 version of this article, see Creating an AEM 6.3 HTML Template Language movie component.

Introduction

You can develop a custom Adobe Experience Manager (AEM) 6.2 HTL tab-based component that lets your web site visitors engage with your site by performing these tasks:

  • view videos
  • view images
  • provide feedback 
  • rate the content
  • learn about information

You can develop the tab-based component by using HTL and AEM digital assets, such as videos and images. The following illustration shows the tab-based movie component developed in this community article.

movie3
The tab movie component

Note:

 Although this component is based on a movie use case, the AEM development concepts discussed in this article can be directly applied to other AEM HTL components that use tabs and display digital assets.

The AEM HTL movie contains these tabs: 

  • Movies
  • Movie Experience
  • Reviews
  • Get in Touch
  • Upcoming

 

Tab configurations

The titles displayed in the component's tabs are all configurable by using a dialog. That is, an author can set them, as shown in this illustration. 

TabsTouch
Tabs located in the Movie component

Movie Details tab

The Movie Experience tab lets an author enter a video that represents a movie trailor. The video is typically located in the AEM DAM. The following list describes the values that an AEM author enters in the dialog: 

  • Trailer Video URL - a JCR path that references a video that represents the movie trailor  (references /content/dam/moviecomponent)
  • Movie Name - the movie name
  • Release Date - the release date
  • Director -the director name
  • Movie Length - the length  of the movie
  • Production House - the company that produced the movie
  • Language - the language of the movie
  • Special Note - describes any additional information about the movie

The following illustration shows this dialog tab. 

 

outputatouch1
The Movie Details Tab

Note:

See the illustration shown at the beginning of this article to see the output generated by the fields in this dialog

The Experience  tab

The Experience tab lets an author enter review information related to the movie. The following illustration shows this dialog. 

tab3Touch
The Experience tab

Reviews tab

The Review view consists of a second tab that represents feedback from the community. The following illustration shows the dialog.

 

tab4touch1
A dialog that lets authors enter review information

Note:

In a real world implementation, the above dialog would be implemented using a Multifield control. For information about Multifields and AEM, see Creating an AEM HTML Template Language component that uses a Multifield. 

Upcoming Movies tab

The Upcoming Movie tab lets an author specify an AEM DAM location and the number of assets to display in the HTL component. The following illustration shows this dialog tab. 

TabUpcomingTouch
Upcoming Movie dialog tab

The Paths to Movies is a pathfield control that specifies the AEM DAM location to where digital assets are located. The number of Random Movies specifies the number of assets that is displayed. In this example, the value is four. The following illustration shows the output generated from this dialog field. 

outputc2
Output generated by the Upcoming Movies dialog tab

Get in Touch dialog

The Get in Touch dialog lets an author specify contact information, such as phone number. The following illustration shows this dialog tab. 

tabupcomingtouch2
The Contact Us dialog tab

The following shows the output for this tab. 

intouch
Output for the Contact Us dialog tab

Install the AEM Movie Component package

Install the AEM packages that are shown at the start of this community article.  For information, see INSTALLING PACKAGES.

Once you install these packages, you can view the following project files. 

projectFiles62
HTL movie component project files

  • A - Touch UI dialog nodes
  • B- Classic UI dialog nodes
  • C- HTL application logic

In addition, the package also installs AEM ClientLibs files in this location. 

/etc/designs/moviecomponent

The following illustration shows the ClientLibs files. 

movieCompDesign
AEM Movie component ClientLib files

The movie-data.zip package contains digital assets used by the component. These assets are placed in this JCR location: 

/content/dam/moviecomponent

Note:

The package contains all of the files that are required to run the Movie component. The rest of this article explains the code, including how to build the OSGi bundle in case you want to change the logic. 

Setup Maven in your development environment

You can use Maven to build an OSGi bundle that the Movie component uses. It is not required to build and deploy the OSGi bundle as it is deployed when you install the AEM package. However, in case you want to modify the OSGi bundle, the article discusses how to build it. 

Maven manages required JAR files that a Java project needs in its class path. Instead of searching the Internet trying to find and download third-party JAR files to include in your project’s class path, Maven manages these dependencies for you.

You can download Maven 3 from the following URL:

http://maven.apache.org/download.html

After you download and extract Maven, create an environment variable named M3_HOME. Assign the Maven install location to this environment variable. For example:

C:\Programs\Apache\apache-maven-3.0.4

Set up a system environment variable to reference Maven. To test whether you properly setup Maven, enter the following Maven command into a command prompt:

%M3_HOME%\bin\mvn -version

This command provides Maven and Java install details and resembles the following message:

OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"

 

Note:

For more information about setting up Maven and the Home variable, see: Maven in 5 Minutes.

Next, copy the Maven configuration file named settings.xml from [install location]\apache-maven-3.0.4\conf\ to your user profile. For example, C:\Users\scottm\.m2\.

You have to configure your settings.xml file to use Adobe’s public repository. For information, see Adobe Public Maven Repository at http://repo.adobe.com/.

The following XML code represents a settings.xml file that you can use.

<?xml version="1.0" encoding="UTF-8"?>

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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.
-->

<!--
 | This is the configuration file for Maven. It can be specified at two levels:
 |
 |  1. User Level. This settings.xml file provides configuration for a single user, 
 |                 and is normally provided in ${user.home}/.m2/settings.xml.
 |
 |                 NOTE: This location can be overridden with the CLI option:
 |
 |                 -s /path/to/user/settings.xml
 |
 |  2. Global Level. This settings.xml file provides configuration for all Maven
 |                 users on a machine (assuming they're all using the same Maven
 |                 installation). It's normally provided in 
 |                 ${maven.home}/conf/settings.xml.
 |
 |                 NOTE: This location can be overridden with the CLI option:
 |
 |                 -gs /path/to/global/settings.xml
 |
 | The sections in this sample file are intended to give you a running start at
 | getting the most out of your Maven installation. Where appropriate, the default
 | values (values used when the setting is not specified) are provided.
 |
 |-->
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ~/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  -->

  <!-- interactiveMode
   | This will determine whether maven prompts you when it needs input. If set to false,
   | maven will use a sensible default value, perhaps based on some other setting, for
   | the parameter in question.
   |
   | Default: true
  <interactiveMode>true</interactiveMode>
  -->

  <!-- offline
   | Determines whether maven should attempt to connect to the network when executing a build.
   | This will have an effect on artifact downloads, artifact deployment, and others.
   |
   | Default: false
  <offline>false</offline>
  -->

  <!-- pluginGroups
   | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
   | when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
   | "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
   |-->
  <pluginGroups>
    <!-- pluginGroup
     | Specifies a further group identifier to use for plugin lookup.
    <pluginGroup>com.your.plugins</pluginGroup>
    -->
  </pluginGroups>

  <!-- proxies
   | This is a list of proxies which can be used on this machine to connect to the network.
   | Unless otherwise specified (by system property or command-line switch), the first proxy
   | specification in this list marked as active will be used.
   |-->
  <proxies>
    <!-- proxy
     | Specification for one proxy, to be used in connecting to the network.
     |
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
  </proxies>

  <!-- servers
   | This is a list of authentication profiles, keyed by the server-id used within the system.
   | Authentication profiles can be used whenever maven must make a connection to a remote server.
   |-->
  <servers>
    <!-- server
     | Specifies the authentication information to use when connecting to a particular server, identified by
     | a unique name within the system (referred to by the 'id' attribute below).
     | 
     | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are 
     |       used together.
     |
    <server>
      <id>deploymentRepo</id>
      <username>repouser</username>
      <password>repopwd</password>
    </server>
    -->
    
    <!-- Another sample, using keys to authenticate.
    <server>
      <id>siteServer</id>
      <privateKey>/path/to/private/key</privateKey>
      <passphrase>optional; leave empty if not used.</passphrase>
    </server>
    -->
  </servers>

  <!-- mirrors
   | This is a list of mirrors to be used in downloading artifacts from remote repositories.
   | 
   | It works like this: a POM may declare a repository to use in resolving certain artifacts.
   | However, this repository may have problems with heavy traffic at times, so people have mirrored
   | it to several places.
   |
   | That repository definition will have a unique id, so we can create a mirror reference for that
   | repository, to be used as an alternate download site. The mirror site will be the preferred 
   | server for that repository.
   |-->
  <mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    <mirror>
      <id>mirrorId</id>
      <mirrorOf>repositoryId</mirrorOf>
      <name>Human Readable Name for this Mirror.</name>
      <url>http://my.repository.com/repo/path</url>
    </mirror>
     -->
  </mirrors>
  
  <!-- profiles
   | This is a list of profiles which can be activated in a variety of ways, and which can modify
   | the build process. Profiles provided in the settings.xml are intended to provide local machine-
   | specific paths and repository locations which allow the build to work in the local environment.
   |
   | For example, if you have an integration testing plugin - like cactus - that needs to know where
   | your Tomcat instance is installed, you can provide a variable here such that the variable is 
   | dereferenced during the build process to configure the cactus plugin.
   |
   | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
   | section of this document (settings.xml) - will be discussed later. Another way essentially
   | relies on the detection of a system property, either matching a particular value for the property,
   | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a 
   | value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
   | Finally, the list of active profiles can be specified directly from the command line.
   |
   | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
   |       repositories, plugin repositories, and free-form properties to be used as configuration
   |       variables for plugins in the POM.
   |
   |-->
  <profiles>
    <!-- profile
     | Specifies a set of introductions to the build process, to be activated using one or more of the
     | mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
     | or the command line, profiles have to have an ID that is unique.
     |
     | An encouraged best practice for profile identification is to use a consistent naming convention
     | for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
     | This will make it more intuitive to understand what the set of introduced profiles is attempting
     | to accomplish, particularly when you only have a list of profile id's for debug.
     |
     | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
    <profile>
      <id>jdk-1.4</id>

      <activation>
        <jdk>1.4</jdk>
      </activation>

      <repositories>
        <repository>
          <id>jdk14</id>
          <name>Repository for JDK 1.4 builds</name>
          <url>http://www.myhost.com/maven/jdk14</url>
          <layout>default</layout>
          <snapshotPolicy>always</snapshotPolicy>
        </repository>
      </repositories>
    </profile>
    -->

    <!--
     | Here is another profile, activated by the system property 'target-env' with a value of 'dev',
     | which provides a specific path to the Tomcat instance. To use this, your plugin configuration
     | might hypothetically look like:
     |
     | ...
     | <plugin>
     |   <groupId>org.myco.myplugins</groupId>
     |   <artifactId>myplugin</artifactId>
     |   
     |   <configuration>
     |     <tomcatLocation>${tomcatPath}</tomcatLocation>
     |   </configuration>
     | </plugin>
     | ...
     |
     | NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
     |       anything, you could just leave off the <value/> inside the activation-property.
     |
    <profile>
      <id>env-dev</id>

      <activation>
        <property>
          <name>target-env</name>
          <value>dev</value>
        </property>
      </activation>

      <properties>
        <tomcatPath>/path/to/tomcat/instance</tomcatPath>
      </properties>
    </profile>
    -->
  

<profile>

                <id>adobe-public</id>

                <activation>

                    <activeByDefault>true</activeByDefault>

                </activation>

                <repositories>

                  <repository>

                    <id>adobe</id>

                    <name>Nexus Proxy Repository</name>

                    <url>http://repo.adobe.com/nexus/content/groups/public/</url>

                    <layout>default</layout>

                  </repository>

                </repositories>

                <pluginRepositories>

                  <pluginRepository>

                    <id>adobe</id>

                    <name>Nexus Proxy Repository</name>

                    <url>http://repo.adobe.com/nexus/content/groups/public/</url>

                    <layout>default</layout>

                  </pluginRepository>

                </pluginRepositories>

            </profile>

</profiles>

  <!-- activeProfiles
   | List of profiles that are active for all builds.
   |
  <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
  </activeProfiles>
  -->
</settings>

Create an Experience Manager archetype project 

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

Maven10
Maven Archetype 10 Project files

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 archetype:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=10 -DarchetypeRepository=https://repo.adobe.com/nexus/content/groups/public/

  3. When prompted, specify the following information:

    • groupId - aemMovie62
    • artifactId - aemMovie62
    • version - 1.0-SNAPSHOT
    • package - com.moviecomponent.service
    • appsFolderName - aemMovie62
    • artifactName - aemMovie62
    • componentGroupName - aemMovie62
    • contentFolderName - aemMovie62
    • cssId - aemMovie62
    • packageGroup -aemMovie62
    • siteName - aemMovie62
  4. When prompted for additional information, specify Y.

  5. Once done, you will see a message like:
    [INFO] Finished at: Wed Mar 27 13:38:58 EDT 2013
    [INFO] Final Memory: 10M/184M

  6. Change the command prompt to the generated project. For example: C:\AdobeCQ\aemMovie62. Run the following Maven 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.

project10

Note:

Delete all packages other than com.moviecomponent.moviecomponent.core.service. Next make sure to delete all generated JAVA files. 

 

Make sure that the only Java files in this package are these ones:

  • Constant - defines constant variables used in this AEM service
  • MovieInterface - defines an operation named getUpcomingMovies that returns a list of upcoming movies
  • MovieService - defines implementation application logic
  • MovieQuery- this class uses the AEM QueryBuilder API to query the AEM DAM

Constant

The Constant class defines constants used by the service. One values is the path in the AEM DAM where the digital assets are stored. The following Java code represents class.

package com.moviecomponent.service.core;

public class Constant {

	public final static String SEARCH_PATH = "/content/dam/moviecomponent/Upcoming/" ;
	public final static String SEARCH_TYPE = "dam:Asset";
	
}

MovieInterface

The following code represents the MovieInterface

package com.moviecomponent.service.core;

import java.util.List;

public interface MovieInterface {

	public List getUpcomingMovies(int numberOfMovies, String path);

}

MovieService

The MovieService extends com.adobe.cq.sightly.WCMUsePojo and represents the entry into the service. This class exposes the activate method that performs initialization task for the movie component. In this use case, the activate method creates an instance of the Movie interface:

service = getSlingScriptHelper().getService(MovieInterface.class);

This class also exposes a method named getFiles that returns an ArrayList instance that contains a list of path locations. Each value represents a path to a digial asset that is displayed in the Upcoming Movies tab of the HTL component. (See the illustration shown at the start of this article.)

The following Java code represents the MovieService class. 

package com.moviecomponent.service.core;

import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.adobe.cq.sightly.WCMUsePojo;
 
public class MovieService extends WCMUsePojo {
    Logger logger = LoggerFactory.getLogger(MovieService.class);
    protected int numberOfMovies;
    protected String path;
 
    MovieInterface service;
    private List<String> file;
 
    @Override
    public void activate() {
 
        this.numberOfMovies = Integer.valueOf(getProperties().get("maxMovies", ""));
        this.path = getProperties().get("moviesPath", "");
         
        service = getSlingScriptHelper().getService(MovieInterface.class);
    }
 
    public List<String> getFiles() {
        try {
            logger.info("The search term is " + this.numberOfMovies);
            this.file = new ArrayList();
            this.file = service.getUpcomingMovies(numberOfMovies, path);
             
        } catch(Exception e) {
 
        } finally {
            return this.file;
        }
    }
 
}

MovieQuery class

The MovieQuery class implements MovieInterface and provides application logic for the getUpcomingMovies method. This method accepts two arguments that are defined the component's dialog:

  • numberOfMovies - the number of items to return. These items are displayed in the Upcoming Section of the HTL component. 
  • pathToMovies - the path in the AEM JCR where the digital assets are located.

The getUpcomingMovies method uses the AEM QueryBuilder API to retrieve a list of digital assets that are used in the HTL component. For information, see QueryBuilder API.

The following Java code represents the MovieQuery class.

package com.moviecomponent.service.core;

import java.util.ArrayList;
import java.util.List;

import javax.jcr.Session;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;

//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.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;

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


@Component
@Service
public class MovieQuery implements MovieInterface {

	Logger logger = LoggerFactory.getLogger(MovieQuery.class);

	private Session session;

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

	@Reference
	private QueryBuilder builder;

	@Override
	public List getUpcomingMovies(int numberOfMovies, String pathToMovies) {

		List<String> hyperLinks = new ArrayList();

		try {

			logger.info("Getting Ready to create SESSION!!");

			// Invoke the adaptTo method to create a Session
			ResourceResolver resourceResolver = resolverFactory
					.getAdministrativeResourceResolver(null);
			session = resourceResolver.adaptTo(Session.class);

			// create query description as hash map
			Map<String, String> map = new HashMap<String, String>();
			map.put("path", pathToMovies);
			map.put("type", Constant.SEARCH_TYPE);
			map.put("p.limit", String.valueOf(numberOfMovies)); // same as
																// query.setHitsPerPage(20)
																// below

			Query query = builder.createQuery(PredicateGroup.create(map),
					session);
			query.setStart(0);

			SearchResult result = query.getResult();

			logger.info("Did we get the result");

			// paging metadata
			int hitsPerPage = result.getHits().size(); // 20 (set above) or
			// lower
			logger.info("Result :"+hitsPerPage);

			// iterating over the results
			for (Hit hit : result.getHits()) {
				String path = hit.getPath();
				// Create a result element
				logger.info(path);
				hyperLinks.add(path);

			}

			
			// close the session
			

		} catch (Exception e) {
			this.logger.info("Something went wrong with session .. {}", e);
		} finally {
			session.logout();
			return hyperLinks;
		}
		
	}

}

Modify the Maven POM file 

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

<dependency>
               <groupId>com.adobe.aem</groupId>
               <artifactId>uber-jar</artifactId>
               <version>6.2.0</version>
               <!-- for AEM6.1 use this version     : <version>6.1.0</version> -->
               <!-- for AEM6.1 SP1 use this version : <version>6.1.0-SP1-B0001</version> -->
               <!-- for AEM6.1 SP2 use this version : <version>6.1.0-SP2</version> -->
               <!-- for AEM6.2 use this version     : <version>6.2.0</version> -->
               <classifier>obfuscated-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>

Modify the POM file to successfully build the OSGi bundle. In the POM file located at C:\AdobeCQ\aemMovie62\core, add the following dependencies.

  • org.apache.felix.scr
  • org.apache.felix.scr.annotations
  • org.apache.jackrabbit
  • org.apache.sling

The following XML represents this POM file.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 |  Copyright 2015 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>aemMovie62</groupId>
        <artifactId>aemMovie62</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <artifactId>aemMovie62.core</artifactId>
    <packaging>bundle</packaging>
    <name>aemMovie62 - Core</name>
    <description>Core bundle for aemMovie62</description>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-scr-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!--
                        <Embed-Dependency>
                            artifactId1,
                            artifactId2;inline=true
                        </Embed-Dependency>
                        -->
                        <Sling-Model-Packages>
                            com.moviecomponent.moviecomponent.core
                        </Sling-Model-Packages>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <!-- Development profile: install only the bundle -->
        <profile>
            <id>autoInstallBundle</id>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.sling</groupId>
                        <artifactId>maven-sling-plugin</artifactId>
                        <configuration>
                            <!-- Note that this requires /apps/aemMovie62/install to exist!!          -->
                            <!--    This is typically the case when ui.apps is deployed first                -->
                            <!--    Otherwise, create /apps/aemMovie62/install manually (CRXDE|Lite)  -->
                            <slingUrlSuffix>/apps/aemMovie62/install/</slingUrlSuffix>
                            <failOnError>true</failOnError>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
   <dependencies>
        <!-- OSGi Dependencies -->
            
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>obfuscated-apis</classifier>
        </dependency>
 
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-atinject_1.0_spec</artifactId>
        </dependency>
        <dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-api</artifactId>
    <version>2.10.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.day.commons.osgi.wrapper</groupId>
    <artifactId>com.day.commons.osgi.wrapper.commons-lang2</artifactId>
    <version>2.5-0001</version>
    <scope>provided</scope>
</dependency>
         <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.scr</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.scr.annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>biz.aQute</groupId>
            <artifactId>bndlib</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.compendium</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>aem-api</artifactId>
        </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-all</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\aemMovie62 folder.

  2. Run the following maven command: mvn clean install.

  3. The OSGi component can be found in the following folder: C:\AdobeCQ\aemMovie62\core\target. The file name of the OSGi component is aemMovie62.core-1.0-SNAPSHOT.jar.

Deploy the bundle to Experience Manager

Deploy the OSGi bundle to Experience Manager by performing these steps:

  1. Login to Apache Felix Web Console at http://server:port/system/console/bundles (default admin user = admin with password= admin).

  2. Click the Bundles tab, sort the bundle list by Id, and note the Id of the last bundle.

  3. Click the Install/Update button.

  4. Browse to the bundle JAR file you just built using Maven. (C:\AdobeCQ\aemMovie62\core\target).

  5. Click Install.

  6. Click the Refresh Packages button.

  7. Check the bundle with the highest Id.

  8. Click Active. Your new bundle should now be listed with the status Active.

  9. If the status is not Active, check the CQ error.log for exceptions.

Movie component dialog

The Movie component lets an author set values, such as the movie trailor video and number of digial assets to display, that are used by the Movie component (see the illustrations shown at the beginning of this article). The following illustration shows the JCR nodes that represents the Movie component's dialog. 

TouchNodes
JCR nodes for the Movie Component Touch UI dialog

PathField

Notice the dialog node located at: 

/apps/moviecomponent/components/content/tab-component/cq:dialog/content/items/movie-details/items/columns/items/video

This node contain a child node named video of type granite/ui/components/foundation/form/pathbrowser. This node is a pathbrowser that lets an author select the JCR path to where the video for the movie trailor is located. This pathbrowser is shown in the following illustration.  

 

touchpathfield
A pathbrowser granite data type

The name property of this node is ./video.  Once this value is set, it is used in the HTL  component to render the video. The following code represents the section of the HTL component that uses the value of the video node. 

<div id="mvideo">

 <video id="my-video" class="video-js" controls preload="auto" width="640" height="264" poster="${properties.fileReference}" data-setup="{}">
 <source src="${properties.video}" type='video/mp4'>
 <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a web browser that
 <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
</p>
</video>

Notice how the value of the video nodes name property is used: 

<source src="${properties.video}"

This is used in a video html element to render the video (the asset in the AEM DAM).  

Note:

This is a benefit of a HTL component. You can use supported HTML5 tags such as the video element with AEM assets to build HTL components. 

Interacting with the MovieService

Notice the node located at: 

/apps/moviecomponent/components/content/tab-component/cq:dialog/content/items/random-movie/items

This node has two child nodes: 

  • movies-path - specifies the path where the digital assets are located for the Upcoming Movies tab. This node is a granite/ui/components/foundation/form/pathbrowser
  • number - specifies the number of assets to display. ThIs node is a granite/ui/components/foundation/form/textfield

These values are retrieved in the MovieService Java class and used in the QueryBuilder API operation. 

this.path = getProperties().get("moviesPath", "");

The HTL component uses the getValue of the MovieService class getFiles method to display digial assets in the Upcoming Movie tab. The following code represents the HTL component that binds to the MovieService.

<div  data-sly-use.v="com.moviecomponent.moviecomponent.core.service.MovieService">
 <p data-sly-list="${v.files}"> 
 <img src="${item}" height=250 width=400 style="padding:4px"></img>
 </p>   
 </div>

Note:

It is recommended that you understand how HTL binds to the MovieService class.   

To bind to a Java class that extends WCMUse, you use a data-sly-use tag.

data-sly-use.v="com.moviecomponent.moviecomponent.core.service.MovieService"

Notice that v is an instance of com.moviecomponent.moviecomponent.core.service.MovieService

Notice the following HTL code. 

 

<p data-sly-list="${v.files}"> 
    <img src="${item}" height=250 width=400 style="padding:4px"></img>
</p> 

In this example, v.files maps to the getFiles method in the MovieService class. This method returns an ArrayList, where each element is a path to the digital asset to display. This is how you iterate through a collection using HTL. Notice that each value is used within an img tag. 

Note:

Most of the other nodes in the dialog are textfield values. The HTL component simply displays these values. For example,  ${properties.contactPerson}

HTML Template Language Movie code

The HTL code is located in this file: 

/apps/moviecomponent/components/content/tab-component/tab-component.html

In this example, all of the values that an author enters into a dialog are displayed. For example, in the Movie tab, the values are displayed by using this code.

<p><span>Movie : ${properties.movieName} <span> </p>
<p>Release Date : ${properties.releaseDate} </p>
<p>Director : ${properties.directors} </p>
<p>Length : ${properties.movieLength}</p>
<p>Production House : ${properties.productionHouse}</p>
<p>Language : ${properties.language}</p>
<p>Note : ${properties.note}</p>
</div>

The following illustration shows the output of this HTL code. 

movie3
Values displayed in a HTL component

The following code represents the entire HTL file. 

<style>
p#experience {
    font-size: 2.2em;
    font-family: sans-serif;
    color: rgba(255, 78, 78, 0.56);
    margin: 15px 4%;
}
p#rating {
font-size: 1.5em;
color: cadetblue;
margin: 15px 4%;
}
p#experiencetext {
font-size: 1.5em;
color: cadetblue;
margin: 15px 4%;
}
div#experiencetext {
font-size: 1.3em;
color: darkgray;
margin: 0px 38px;
}
#rating span {
line-height: 35px !important;
color: #234567 !important;
}
p#reviewer {
margin: 0px 50px;
color: grey;
}
div#reviewtext {
padding: 5% 3% 0% 3%;
color: rgba(207, 47, 47, 0.7);
font-size: 1.4em;
font-family: initial;
}
#mvideo {
padding-left: 150px;
    padding-top: 41px;
    border: 1px solid #456722;
    padding-bottom: 41px;
}
div#moviedata {
    margin: 0px 150px;
}
#moviedata p {
    color: brown;
    font-size: 1.3em;
}
#moviedata span {    font-size: 2.0em;
    font-family: fantasy;
    line-height: 50px !important;
    color: rgb(211, 234, 154) !important;
}
</style>
<section role="main">

    <div class="container">

        <div class="row">
            <div class="twelve columns">
                <article>
                    <h2 style="margin: 6px 5%">${properties.header}</h2>
                    <div class='tabs tabs_animate'>
                        <ul class='horizontal'>
                            <li><a href="#tab-1">${properties.movieDetails}</a>
                            </li>
                            <li><a href="#tab-2">${properties.experience}</a>
                            </li>
                            <li><a href="#tab-3">${properties.reviews}</a>
                            </li>
                            <li><a href="#tab-4">${properties.contact}</a>
                            </li>
                            <li><a href="#tab-5">${properties.random}</a>
                            </li>
                        </ul>
                        <div id='tab-1'>
                        <div data-sly-test="${properties.fileReference && properties.video }" id="mvideo">

                            <video id="my-video" class="video-js" controls preload="auto" width="640" height="264" poster="${properties.fileReference}" data-setup="{}">
                                <source src="${properties.video}" type='video/mp4'>
                                        <p class="vjs-no-js">
                                            To view this video please enable JavaScript, and consider upgrading to a web browser that
                                            <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
                                        </p>
                            </video>
                            </div>
                            <div id="moviedata">
                                <p><span>Movie : ${properties.movieName} <span> </p>
                                <p>Release Date : ${properties.releaseDate} </p>
                                <p>Director : ${properties.directors} </p>
                                <p>Length : ${properties.movieLength}</p>
                                <p>Production House : ${properties.productionHouse}</p>
                                <p>Language : ${properties.language}</p>
                                <p>Note : ${properties.note}</p>
                            </div>
                        </div>
                        <div id='tab-2'>
                            <p id="experience">Our Experience with ${properties.movieName}</p>
                            <p id="rating">Ratings <img src="/content/dam/moviecomponent/rating/${properties.rating}-star.png" />
                            </p>
                            <p id="rating">Experience..... </p>
                            <div id="experiencetext"> ${properties.movieExperience @ context="html"}</div>
                        </div>
                        <div id='tab-3'>

                            <div id="reviewtext"><i> ${properties.review2description @ context="html"}</i>
                            </div>
                            <p id="reviewer"> -- by ${properties.review1person}</p>



                            <div id="reviewtext"><i> ${properties.review2description @ context="html"}</i>
                            </div>
                            <p id="reviewer"> -- by ${properties.review2person}</p>

                        </div>
                        <div id='tab-4'>
                        <div id="experiencetext">${properties.contactDescription}</div>
                            <p id="rating"><span>Contact Person Name </span> ${properties.contactPerson}</p>
                            <p id="rating"><span>Contact Person Position </span>${properties.contactPosition}</p>
                            <p id="rating"><span>Contact Person Email </span>${properties.contactEmail}</p>
                            <p id="rating"><span>Contact Person Number </span>${properties.contactNumber}</p>

                        </div>
                        <div id='tab-5'>
                        <div data-sly-test="${ !properties.maxMovies || !properties.moviesPath}">
                        <p style="margin-left:150px;margin-top:40px"> Please author Path to Movies and Number of Movies</p>
                        </div>
                        <div data-sly-test="${properties.maxMovies && properties.moviesPath}" data-sly-use.v="com.moviecomponent.moviecomponent.core.service.MovieService">
                            <p data-sly-list="${v.files}"> 
                                <img src="${item}" height=250 width=400 style="padding:4px"></img>
                            </p>   
                        </div>

                        </div>
                  </div>
                </article>
                </div>
            </div>



        </div>
</section>

Running the Movie component

You can run the Movie component by going to the Touch UI view at: 

http://localhost:4502/sites.html/content/moviecomponent/en

as shown in this illustration. (Make sure that you install the package that is shown at the start of this article.)

 

touchuiview
The AEM Touch UI view

Next open the  page in Touch UI view at this location: 

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

You will see the movie component. You can edit it within the Touch UI like other AEM components. 

Note:

There are other supporting files that are part of /apps/moviecomponent not covered in this article. For example, if you look at files under /apps/moviecomponent/components/structure, you will see some boilerplate files that setup pages, footers, and so on. It is recommended that you look at this files so you understand how they all work together. 

See also

Congratulations, you have just read about how to develop a dynamic AEM Movie component that uses AEM digital assets. Please refer to the AEM community page for other articles that discuss how to build AEM services/applications.

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