0% found this document useful (0 votes)
544 views

Customize Adobe Experience Manager

AEM

Uploaded by

Vincent Valiant
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
544 views

Customize Adobe Experience Manager

AEM

Uploaded by

Vincent Valiant
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 422

Extend and Customize Adobe

Experience Manager

student guide

Extend and Customize Adobe Experience Manager 1 Be


ADOBE DIGITAL LEARNING SERVICES
Aleksander Dultsev
©2018 Adobe Systems Incorporated. All rights reserved.

Extend and Customize Adobe Experience Manager

If this guide is distributed with software that includes an end user agreement, this guide, as well as the software described in it,
is furnished under license and may be used or copied only in accordance with the terms of such license. Except as permitted
by any such license, no part of this guide may be reproduced, stored in a retrieval system, or transmitted, in any form or by
any means, electronic, mechanical, recording, or otherwise, without the prior written permission of Adobe Systems
Incorporated. Please note that the content in this guide is protected under copyright law even if it is not distributed with
software that includes an end user license agreement.

The content of this guide is furnished for informational use only, is subject to change without notice, and should not be
construed as a commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsibility or
liability for any errors or inaccuracies that may appear in the informational content contained in this guide.

ADOBE COPYRIGHT PROTECTED


Please remember that existing artwork or images that you may want to include in your project may be protected under
copyright law. The unauthorized incorporation of such material into your new work could be a violation of the rights of the
copyright owner. Please be sure to obtain any permission required from the copyright owner.

Any references to company names in sample templates are for demonstration purposes only and are not intended to refer to
any actual organization.

Adobe, the Adobe logo, Acrobat, the Creative Cloud logo, and the Adobe Marketing Cloud logo are either registered
trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries.

All other trademarks are the property of their respective owners.

Adobe Systems Incorporated, 345 Park Avenue, San Jose, California 95110, USA.

Notice to U.S. Government End Users. The Software and Documentation are “Commercial Items,” as that term is defined at 48
C.F.R. §2.101, consisting of “Commercial Computer Software” and “Commercial Computer Software Documentation,” as such
terms are used in 48 C.F.R. §12.212 or 48 C.F.R. §227.7202, as applicable. Consistent with 48 C.F.R. §12.212 or 48 C.F.R.
§§227.7202-1 through 227.7202-4, as applicable, the Commercial Computer Software and Commercial Computer Software
Documentation are being licensed to U.S. Government end users (a) only as Commercial Items and (b) with only those rights
as are granted to all other end users pursuant to the terms and conditions herein. Unpublished-rights reserved under the
copyright laws of the United States. Adobe agrees to comply with all applicable equal opportunity laws including, if
appropriate, the provisions of Executive Order 11246, as amended, Section 402 of the Vietnam Era Veterans Readjustment
Assistance Act of 1974 (38 USC 4212), and Section 503 of the Rehabilitation Act of 1973, as amended, and the regulations at 41
CFR Parts 60-1 through 60-60, 60-250, and 60-741. The affirmative action clause and regulations contained in the preceding
sentence shall be incorporated by reference.

September 19, 2018

Extend and Customize Adobe Experience Manager 2


Table of Contents
Module 1: Adobe Experience Manager Technical Basics ............................................................................................................................................ 6
Fluid Experiences.......................................................................................................................................................................................................................... 7
AEM Architecture.......................................................................................................................................................................................................................... 8
Module 2: AEM Installation ......................................................................................................................................................................................................... 13
Graphical and Command-Line Methods to Install AEM...................................................................................................................................... 15
Exercise 1: Install AEM author and publish instances ............................................................................................................................................ 18
Module 3: Authoring Basics ....................................................................................................................................................................................................... 29
AEM Sites Pages ......................................................................................................................................................................................................................... 32
Exercise 1: Create and edit a page in AEM................................................................................................................................................................... 34

ADOBE COPYRIGHT PROTECTED


Module 4: Developer Tools........................................................................................................................................................................................................ 45
Web Console .................................................................................................................................................................................................................................47
Exercise 1: Install, create, build, and download a package................................................................................................................................. 50
References ..................................................................................................................................................................................................................................... 60
Module 5: Configuring AEM Development Environment ........................................................................................................................................ 61
Working with Maven ................................................................................................................................................................................................................ 62
Installing and Configuring Eclipse...................................................................................................................................................................................... 66
Exercise 1: Install and configure Eclipse (Optional)................................................................................................................................................ 68
Exercise 2: Install and configure the Eclipse AEM plug-in (Optional) .......................................................................................................... 72
Exercise 3: Create an AEM project ................................................................................................................................................................................... 76
Building and Deploying a Project Using Maven ...................................................................................................................................................... 83
Exercise 4: Build and deploy the project to AEM .................................................................................................................................................... 84
Exercise 5: Configure the AEM Server in Eclipse ..................................................................................................................................................... 89
References ..................................................................................................................................................................................................................................... 96
Module 6: OSGi Configurations and Run Modes............................................................................................................................................................97
OSGi Configurations................................................................................................................................................................................................................. 98
Methods to Create OSGi Configurations ...................................................................................................................................................................... 98
Run Modes .................................................................................................................................................................................................................................. 103
Exercise 1: Create custom OSGi configurations and start AEM with a custom run mode ..............................................................107
Answers to Questions .............................................................................................................................................................................................................115
References .................................................................................................................................................................................................................................... 116
Module 7: Configuring Custom Loggers in AEM ............................................................................................................................................................ 117
AEM Logging System .............................................................................................................................................................................................................. 118
Loggers and Writers ................................................................................................................................................................................................................ 120
Creating Your Own Loggers and Writers ...................................................................................................................................................................... 122
Exercise 1: Observe project logger config node....................................................................................................................................................... 123
Exercise 2: Create custom loggers...................................................................................................................................................................................128
Module 8: Deep Dive into OSGi.............................................................................................................................................................................................. 132
OSGi Architecture......................................................................................................................................................................................................................133
Annotations in OSGi.................................................................................................................................................................................................................143
Configurable Services ............................................................................................................................................................................................................ 146

Extend and Customize Adobe Experience Manager 3


Exercise 1: Implement a bundle activator .................................................................................................................................................................. 148
Exercise 2: Create and use a custom service..............................................................................................................................................................151
Exercise 3: Code OSGi configurations ........................................................................................................................................................................... 161
References ....................................................................................................................................................................................................................................170
Module 9: Working with the Sling Web Framework ................................................................................................................................................... 171
Understanding Sling Resolution Process ...................................................................................................................................................................... 172
Working with Sling Servlets ................................................................................................................................................................................................ 179
Configuring the Sling POST Servlet ............................................................................................................................................................................... 180
Exercise 1: Create a Sling Servlet ......................................................................................................................................................................................182
Exercise 2: Add content with Sling post servlet...................................................................................................................................................... 188
Creating System Users .......................................................................................................................................................................................................... 195
Exercise 3: Create a system user ..................................................................................................................................................................................... 198
References .................................................................................................................................................................................................................................. 206

ADOBE COPYRIGHT PROTECTED


Module 10: Access DataLayer in Adobe Experience Manager using Sling .................................................................................................. 207
Understanding ResourceResolver .................................................................................................................................................................................. 208
Working with Sling Models ................................................................................................................................................................................................. 210
Sling Model Implementation ...............................................................................................................................................................................................211
Injector Specific Annotations ............................................................................................................................................................................................. 213
Exercise 1: Create a Sling Model ....................................................................................................................................................................................... 215
Sling Model Exporter .............................................................................................................................................................................................................229
Exercise 2: Extend a core component..........................................................................................................................................................................230
Query Index.................................................................................................................................................................................................................................239
Oak Query Implementation Overview ....................................................................................................................................................................... 240
Configuring the Indexes ....................................................................................................................................................................................................... 242
Search Basics .............................................................................................................................................................................................................................. 247
Exercise 3: Search resources with queries .................................................................................................................................................................249
Module 11: Event Handling in Adobe Experience Manager ................................................................................................................................. 253
Sling Events.................................................................................................................................................................................................................................. 254
Listening to OSGi Events....................................................................................................................................................................................................... 255
Exercise 1: Handle OSGi Events ....................................................................................................................................................................................... 257
Working with Sling Jobs...................................................................................................................................................................................................... 264
Exercise 2: Create a job consumer for a topic ........................................................................................................................................................ 266
Sling Scheduler ......................................................................................................................................................................................................................... 270
Exercise 3: Clean up nodes with a Sling scheduler ............................................................................................................................................... 271
Extra Credit Exercise: Add more Cleanup paths .................................................................................................................................................... 278
Sling Resource Change Listening ................................................................................................................................................................................... 279
Exercise 4: Create a resource listener............................................................................................................................................................................281
References ................................................................................................................................................................................................................................... 287
Module 12: Deep Dive into AEM APIs.................................................................................................................................................................................288
Polling Importers ..................................................................................................................................................................................................................... 289
Exercise 1: Creating a polling importer ....................................................................................................................................................................... 290
Creating AEM Pages and Assets Programmatically............................................................................................................................................ 299
Exercise 2: Programmatically create an AEM page ............................................................................................................................................ 300
Exercise 3: Create AEM pages programmatically................................................................................................................................................. 308
AEM Projects ............................................................................................................................................................................................................................... 317
Exercise 4: Create a simple project .................................................................................................................................................................................318

Extend and Customize Adobe Experience Manager 4


Executing an Existing Workflow...................................................................................................................................................................................... 322
Exercise 5: Customize the process step.......................................................................................................................................................................326
References .................................................................................................................................................................................................................................. 340
Module 13: Managing Users, Groups, and Rights in AEM .....................................................................................................................................341
Working with Users, Groups, and Access Control Lists ......................................................................................................................................... 342
Permissions and ACLs ............................................................................................................................................................................................................343
Exercise 1: Create a new user ........................................................................................................................................................................................... 346
Exercise 2: Create a new group....................................................................................................................................................................................... 349
Exercise 3: Add the user to the group ...........................................................................................................................................................................351
Exercise 4: Create a custom AEM project with ACL automation ................................................................................................................. 353
References .................................................................................................................................................................................................................................... 371
Module 14: Writing Tests............................................................................................................................................................................................................ 372

ADOBE COPYRIGHT PROTECTED


Understanding Testing Frameworks .............................................................................................................................................................................. 373
Jenkins for Continuous Integration ................................................................................................................................................................................. 376
Exercise 1: Create unit tests using Mockito ............................................................................................................................................................... 377
Exercise 2: Create unit tests using Sling Mocks ..................................................................................................................................................... 380
Exercise 3: Create unit tests using AEM Mocks ..................................................................................................................................................... 386
References and Helpful Links: ..........................................................................................................................................................................................393
Appendix A: Creating Project and Workflow................................................................................................................................................................. 394
Exercise 1: Create a project template............................................................................................................................................................................395
Exercise 2: Execute a workflow....................................................................................................................................................................................... 403
Exercise 3: Build and test a workflow.......................................................................................................................................................................... 406

Extend and Customize Adobe Experience Manager 5


Module 1: Adobe Experience Manager
Technical Basics

Introduction

Adobe Experience Manager (AEM) is a content management system that helps you build and

ADOBE COPYRIGHT PROTECTED


manage online content. It is a Java-based web application that is built using different technologies
and has a
user-friendly and flexible User Interface (UI). This helps customers build robust and scalable
applications.
In order to use AEM capabilities effectively, you should first understand the architecture, UI, basic
authoring features, and the administrative consoles of AEM.
Objectives

• Explain fluid experiences in AEM

• Explain the architecture of AEM

• Explain different types of AEM instances

• Install and run AEM instances by using the graphical method

• Install and run AEM instances by using the command line method

• Navigate through the AEM UI

• Create and edit pages

• Explain the features and UI elements of the AEM developer tools

• Install a package in AEM

• Create, build, and download a package

Extend and Customize Adobe Experience Manager 6


Fluid Experiences
AEM enables organizations to create seamless digital experiences for their customers. It helps
design, anticipate, and deliver rapidly adaptable experiences across web, mobile, in-store, and any
end point in the customer journey.
Customers use offline and online touch points to engage with an organization’s products and
services. It is important that organizations provide a fluid experience across multiple touch points.
This increases customer engagement through various channels.
AEM helps you deliver these fluid experiences by supporting true omnichannel experiences across
owned, earned, and paid channels. Content goes beyond the traditional “web” form and is

ADOBE COPYRIGHT PROTECTED


available in a multitude of end points – screens, dynamic adaptive forms, documents, email, social,
apps, and more.
The Content Services feature in AEM enables you to reuse web content in mobile app, single page
applications and custom applications.
You can integrate AEM with Adobe Target to create personalized and targeted content. You can
integrate AEM with Adobe Analytics to understand how visitors to a page have interacted with that
page. This provides information about what you need to test and optimize to enhance customer
experience.

Extend and Customize Adobe Experience Manager 7


AEM Architecture
AEM is a web-based client-server system, made up of several infrastructure-level and application-
level functions. These functions are used to build relevant applications.
Basics of AEM Architecture Stack

AEM architecture stack is based on technologies such as OSGi, Java Content Repository (JCR), and
Apache Sling.
The following diagram depicts a high-level view of the AEM architecture stack:

ADOBE COPYRIGHT PROTECTED


Granite Platform

Granite is a general-purpose platform for building robust scalable applications. It is Adobe's open
web stack, and forms the technical foundation on which AEM is built. Granite supports an open
architecture, which is based on both open standards (JCR and OSGi) and open source projects
(Apache Sling and Apache Jackrabbit).

Note: Granite is an open development project within Adobe but not an open source project.

Technically, at the core, Granite provides:


• An application launcher for a standalone Java or Web application archive for deployment in the
existing servlet containers or application servers.

• An OSGi Framework into which all applications are deployed.

• OSGi Compendium Services to support building applications, such as Log Service, Http Service,
Event Admin Service, Configuration Admin Service, Declarative Services, and Metatype Service.
Extend and Customize Adobe Experience Manager 8
• A comprehensive Logging Framework providing various logging APIs, such as SLF4J, Log4F,
Apache Commons Logging, and OSGi Log Service.

• A repository based on Apache Jackrabbit Oak and JSR-283.

• The Apache Sling Web Framework.

Granite UI

The consoles in the main navigation, tools, and editors of AEM are built using the Granite UI. The
Granite UI provides a foundational UI framework for:
• UI widgets

ADOBE COPYRIGHT PROTECTED


• Extensible and plug-in-based admin UI

It also adheres to the following requirements:


• Mobile first (designing an online experience for mobile before designing it for the desktop)

• Extensible

• Easy to override

Note: The Granite UI is based on Coral 3, which is Adobe’s universal UI across all products.

OSGi Framework

OSGi enables a collaborative and modular environment, where each application may be built and
implemented as a small bundle. Each bundle is a collection of tightly coupled, dynamically
loadable classes, JAR files, and configuration files that explicitly declare their external dependencies.

Note: OSGi used to stand for Open Service Gateway Initiative, but that name has been discontinued,
and it is now officially no longer an abbreviation. It is just known in the industry as OSGi.

All content is stored in the content repository, which means backup is done at the repository level.
OSGi runtime hosts Java applications that can access the repository by using the JCR API. As part of
the application runtime, you get Apache Sling, a RESTful web application framework that exposes
the full repository content using HTTP and other protocols.

Extend and Customize Adobe Experience Manager 9


The following diagram depicts the AEM platform foundation of Granite and the OSGi framework:

ADOBE COPYRIGHT PROTECTED


Apache Felix

Apache Felix is an open source implementation of OSGi for the AEM framework. It provides a
dynamic runtime environment, where the code and content bundles can be loaded, unloaded, and
reconfigured at runtime.
JCR

The JCR, specifically Java Specification Request-283 (JSR- 283), is a database that supports
structured and unstructured content, versioning, and observation. In other words, it is a database
that resembles a file system.

Note: The Adobe implementation of JSR-283 was known as the Content Repository eXtreme (CRX).
Hence, you may see CRX in some tools and interfaces in AEM. However, the CRX as a feature is being
phased out. In its place, AEM uses the Granite platform and Apache Jackrabbit Oak.

All data pertaining to AEM, such as HTML, HTML Template Language (HTL), CSS, JavaScript/Java,
images, and videos are stored in the JCR object database. JCR is built with Apache Jackrabbit
Oak, an open-source project.
AEM also works with other JCR repositories, such as Apache Jackrabbit 2.x, and with a number of
non-JCR data stores through connectors. Therefore, it is capable of pulling content from its built-
Extend and Customize Adobe Experience Manager 10
in Oak repository and any JCR- compliant source, such as a third-party repository (for example,
Jackrabbit 2.x) or a connector that exposes the legacy storage through the JCR.
The advantages of using JCR are:
• It provides a generic application data store for structured and unstructured content. While file
systems provide excellent storage for unstructured and hierarchical content, the databases provide
storage for structured data. This way, JCR provides the best of both the data storage architectures.

• It supports namespaces. Namespaces prevent naming collisions among items and node types that
come from different sources and application domains. JCR namespaces are defined with a prefix,
delimited by a single colon (:). For example, jcr:title. This means that this title property is defined in
the jcr namespace.

ADOBE COPYRIGHT PROTECTED


• It provides one interface to interact with text and binary data. This helps in easy access and
management of data in comparison to storing it in multiple places.

Apache Sling

Apache Sling is a web application framework for content-centric applications and uses a JCR, such
as Apache Jackrabbit Oak, to store and manage content.
The key features of Apache Sling include:
• It is Apache open source.

• It is based on REST principles and helps build applications as a series of OSGi bundles.

• It is resource-oriented (every resource has a URI) and maps to JCR nodes.

A request URL is first resolved to a resource, and then based on the resource, Apache Sling selects
the Servlet or script to handle that request. Servlets and scripts are handled as resources and are
accessible by a resource path. This means every script, Servlet, filter, and error handler is available
from the Resource Resolver just like normal content—providing data to be rendered on request.

Extend and Customize Adobe Experience Manager 11


Application-Level Functions

At the application-level, AEM has the following functions:


• AEM Sites (Web Experience Management, Digital Signage (Screens), e-Commerce, and Online
Communities)

• AEM Assets (Digital Asset Management, Assets Sharing and Dynamic Media Delivery)

• AEM Forms (Online Forms Management & Dynamic Customer Communications)

• Managed Services (Adobe-hosted operations for AEM deployments)

• Content Services

ADOBE COPYRIGHT PROTECTED


• Multi-Site Manager

• Workflows

The above application functions/modules sit on top of the Granite platform. They share the same
infrastructure and UI framework. These modules are encapsulated within the OSGi container and
are very tightly integrated with each other.

Extend and Customize Adobe Experience Manager 12


Module 2: AEM Installation
AEM runs on most operating systems that support the Java platform. All client interactions with
AEM are done through a web browser.
Instances

In AEM terminology, an instance is a copy of AEM running on a server. AEM installations usually
involve running at least two instances:
• Author: An AEM instance used to create, upload, and edit content and administer the website.

ADOBE COPYRIGHT PROTECTED


After the content is ready to go live, it is replicated to the publish instance.

• Publish: An AEM instance that serves the published content to the public.

The following graphic depicts a typical AEM implementation:

Note: The Dispatcher is a static web server, such as Apache httpd and Microsoft IIS, augmented
with the AEM Dispatcher module. It caches webpages produced by the publish instance to improve
performance.

Extend and Customize Adobe Experience Manager 13


Installation Prerequisites

To install AEM, you need:


• AEM installation and startup JAR file (also known as the quickstart file)

• A valid AEM license key properties file

• JDK version 1.8

• Approximately 4 GB of free space per instance

• Minimum 4 GB of RAM

During installation, you will notice that the JAR file creates a root folder on your system called

ADOBE COPYRIGHT PROTECTED


crx-quickstart.

Note: After installation is complete, the quickstart file is referred to as the AEM startup file.

Note: Windows users may need to ensure their system’s environment variables are set
appropriately to run Java 1.8 before installing AEM. Open a command prompt and type java -
version to ensure Java is set up properly. When you run the command, it should show the
following result
(java version “1.8.0_144”):

Extend and Customize Adobe Experience Manager 14


Graphical and Command-Line Methods to Install AEM
There are many ways of installing and starting an AEM instance, two of which are—graphical and
command line.
Graphical Method

This method involves using the *.jar file to start an AEM instance. In a Windows or Mac OS
environment, you can double-click the aem-author-4502.jar file to start an author instance, or the
aem-publish-4503.jar file to start a publish instance.
The installation will take approximately 5-7 minutes the first time, depending on your system’s
capabilities.

ADOBE COPYRIGHT PROTECTED


A dialog box similar to the following (also known as the GUI) will pop up:

After AEM starts, your default browser will open a new tab automatically, pointing to AEM’s start
URL (where the port number is the one you defined on installation).
Command Line Method

When you use the command line method to install and start, you can provide additional
performance-tuning parameters to the Java Virtual Machine (JVM) and perform other
administrative tasks. On Windows, MacOS X, or *x, you can increase the Java heap size during the
installation, which improves performance.

• Using the Command Line to Install and Start an AEM Author Instance
Prior to the installation, you may want to know which parameters are available to configure
quickstart. Enter the following command in the command prompt to display a complete list of
optional parameters:
java -jar aem-author-4502.jar -h
The AEM quickstart installer will show all the available command-line options without starting the
server.

Extend and Customize Adobe Experience Manager 15


In addition, you need to tune the JVM used for running AEM. Tuning the JVM is an important and
delicate task and requires a more realistic environment in terms of resources (hardware and the
operating system) and workload (content and requests). You can start your instance (author or
publish) by using the following parameters:
-Xms --> assigns the initial heap size

Default value 64 MB for a JVM running on 32-bit machines, or 83 MB for 64-bit machines

Recommended Specific to physical memory available and expected traffic

Syntax -Xms512m (sets the initial heap size to 512 MB)

ADOBE COPYRIGHT PROTECTED


-Xmx --> assigns the maximum size to which the heap can grow
Default value 64 MB for a JVM running on 32-bit machines, or 83 MB for 64-bit machines
Specific to physical memory available and expected traffic, but should be equal or
Recommended greater than the initial size. To run AEM, it is recommended to allocate at least
1024 MB of heap size.
-Xmx1024m (sets the maximum size for the heap. In the example, we are letting it
Syntax grow to 1024 MB. However, in production, this should be higher because AEM
consumes a lot of resources).

Example:
java -Xms512m -Xmx1024m -jar aem-author-4502.jar

• Using the Command Line to Install and Start an AEM Publish Instance
If you want to install and start your AEM publish instance using a command prompt, navigate to
the directory containing your quickstart jar file (such as \adobe\AEM\publish), and enter the
following command to install the publish instance:
java -jar aem-publish-4503.jar

• Using the Command Line to Start AEM with the nosamplecontent Run Mode
A run mode is a collection of AEM configuration parameters that allow you to tune your AEM
instance for a specific purpose. nosamplecontent is a predefined run mode available in AEM.

Note: The author and publish instances are the same software stack but two different run
modes.

Extend and Customize Adobe Experience Manager 16


Use nosamplecontent in your command line the first time you install an AEM instance to install it
without any sample content (which includes the reference site content):
java -jar aem-author-4502.jar -r author, nosamplecontent -gui
Note the use of the -r author parameter. This tells AEM to set this instance as the author run mode.
The -gui option turns on the GUI mode that shows the AEM icon window on your system.
Therefore, the instructions you provided here specify two run modes: author and
nosamplecontent. The syntax to specify multiple run modes is:
-r runmode1, runmode2, …

ADOBE COPYRIGHT PROTECTED


You may use this option to install on a production environment, which does not require this
reference site content. Also, note the nosamplecontent option is only available on the first
installation of the instance.
Run modes are covered in an additional training course in more depth (OSGi Configurations & Run
Modes).
Additional Method to Start AEM: Batch Files

After you install AEM using a graphical or command line method, you can use the built-in batch
files in the crx-quickstart directory to start AEM. You can also configure the start up and additional
options using these batch files.
Navigate to your install directory and then to the crx-quickstart directory that was created as part of
your installation. Within \crx-quickstart\bin subdirectory, there are batch files (on Windows
systems) available for you to start or stop AEM, and get status information on the server. The server
parameters provided in these batch files are documented using comments or commented-out as
appropriate in the case of options that you may want to set for your instances.

Extend and Customize Adobe Experience Manager 17


Exercise 1: Install AEM author and publish instances
Scenario: As a developer or administrator, you need to install and start/stop a development
instance of AEM on a local machine, using both the quickstart JAR file and command line method.
Task 1.1: Install and start an AEM author instance using the graphical method

In this task, you will install and start your AEM author instance on port 4502.

Note: If you are attending a v/ILT class using ReadyTech, steps 1 through 3 were completed for
you. Skip ahead to step 4.

ADOBE COPYRIGHT PROTECTED


To install an AEM author instance:
1. Create a folder structure on your file system where you will store, install, and start your AEM
author instance. For example, in:

a. Windows: C:/adobe/AEM/author

b. MacOS X: /Applications/adobe/AEM/author or *x: /opt/adobe/AEM/author

2. Copy the aem-quickstart-6.4.0.jar and license.properties files, from the location provided
by your instructor, to your newly created directory.

3. Rename the aem-quickstart-6.4.0.jar file to aem-author-4502.jar:

• aem = Application
• author = Web Content Management (WCM) mode AEM will run in (in this
case, author)
• 4502 = Port AEM will run in

4. You can, therefore, control the way AEM is installed by defining properties in a filename.

Extend and Customize Adobe Experience Manager 18


5. Double-click the aem-author-4502.jar file (located at C:\adobe\AEM\author in Windows, if
you are using ReadyTech). Installation will take approximately 5–7 minutes depending on
your system’s capabilities.

Note: When running for the first time, the quickstart *.jar will notice that it has to install
AEM.

By renaming the file, you use a convention of passing the instance name
(Webpathcontext) and port number through the file name, so no user interaction is
needed during the installation process.

If no port number is provided in the file name, AEM will select the first available port from

ADOBE COPYRIGHT PROTECTED


the following list in this specific order: 1) 4502, 2) 8080, 3) 8081, 4) 8082, 5) 8083, 6) 8084,

6. After the AEM Author instance has started successfully, the start-up screen (the GUI) will
change to something similar to the following:

7. You have now completed the installation of AEM.

Note: To stop the AEM instance, click the ON/OFF toggle button in the GUI.

8. Now, each time you want to run your AEM author instance, follow the same procedure in
step 4 to start it. However, this time, the startup will be as fast as one minute or less, as the
initial installation task has been performed.

9. In addition, after AEM starts, your default browser will automatically open to AEM’s start URL
(where the port number is the one you defined on installation). For example:
http://localhost:4502

Extend and Customize Adobe Experience Manager 19


10. A sign in screen is displayed as shown below:

ADOBE COPYRIGHT PROTECTED


11. Enter the user name and password, and click Sign In. If you are using a ReadyTech machine
or local installation, use the following credentials to sign in:

12. User name: admin


13. Password: admin

Note: Notice, a crx-quickstart directory is also created on your computer as shown below:

This is the extracted repository that is created upon installation of AEM.

Extend and Customize Adobe Experience Manager 20


Task 2: Install and start an AEM publish instance using the graphical method

In this task, you will install and start an AEM publish instance on port 4503. The publish instance is
a separate run mode where your published content resides for access on the web.

Note: If you are attending a v/ILT class using ReadyTech, steps 1 through 3 were completed for
you. Skip ahead to step 4.

To install an AEM publish instance:


1. Create a folder structure on your file system where you will store, install, and start your AEM
publish instance. For example, in:

ADOBE COPYRIGHT PROTECTED


a. Windows: Create C:/adobe/AEM/publish

b. MacOS X: Create /Applications/adobe/AEM/publish or *x:


/opt/adobe/AEM/publish

2. Copy the aem-quickstart-6.4.0.jar and license.properties files, from the location provided
by your instructor, to your newly created directory.

3. Rename the aem-quickstart-6.4.0.jar file to aem-publish-4503.jar:

• aem = Application
• publish = Web Content Management (WCM) mode AEM will run in (in this
case, publish)
• 4503 = Port AEM will run in

Note: If you have multiple author and publish instances, a best practice to consider is using
an even/odd numbering paradigm for port numbers.
So, your author instances would be:
• 4502, 4504, 4506, …
And, your publish instances would be:

• 4503, 4505, 4507, …

Extend and Customize Adobe Experience Manager 21


4. Double-click the aem-publish-4503.jar file (located at C:\adobe\AEM\publish in Windows, if
you are using ReadyTech). Installation will take approximately 5–7 minutes depending on
your system’s capabilities.

5. After the initial installation, each time you start an AEM instance (author or publish), it will
take 1-2 minutes.

6. After the AEM publish instance has started successfully on port 4503, the start-up screen (the
GUI) will change to something like the following:

ADOBE COPYRIGHT PROTECTED


7. The AEM We.Retail page opens in a new tab in your default browser (where the port
number is the one you defined on installation). For example, http://localhost:4503

Note: We.Retail is a reference implementation that illustrates the recommended way of


setting up an online presence with AEM. While We.Retail illustrates a retail vertical, the way
the site is set up can be applied to any vertical. Only the product catalog and cart features
are retail specific.

Tip: You do not need to manually sign in as the publish instance loads the
We.Retail reference site immediately.

Extend and Customize Adobe Experience Manager 22


You have now successfully installed and started both AEM author and AEM publish instances on
localhost. To start the AEM instance in future, double-click the renamed *.jar file (in Windows).

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 23


Task 3: Start an AEM author instance using the command line method

In this task, you will start and stop an AEM author instance using the command line method.

Note: You already have an author instance and a publish instance running. To stop your author instance,
click the ON/OFF toggle button in the GUI window.

ADOBE COPYRIGHT PROTECTED


To start an instance:
8. Open a command prompt in the directory where your quickstart *.jar file is located to run
the author instance. If you are using ReadyTech, this is C:\adobe\AEM\author.

Tip: To open a directory in Windows Explorer in the command-line, select


the directory, hold down the Shift key, and right-click. Then, you will see an
option (Open command window here) to open that directory in a

9. Prior to the installation, you may want to know which parameters are available to configure
the AEM quickstart. Enter the following command to display a complete list of optional
parameters you can use to install and start AEM (this command will not install and start
AEM):

java -jar aem-author-4502.jar -h


Tip: The command may not work if your quickstart file is named differently. If you are using a
ReadyTech instance for this training, the filename should be the same as shown in the command
above. If you are not using ReadyTech, your quickstart filename may differ, so anytime you run the
commands, ensure you use the exact filename.

Extend and Customize Adobe Experience Manager 24


10. Start your author instance again by allocating 1024 MB to the JVM by using this command:
java -Xmx1024m -jar aem-author-4502.jar -gui

11. Your AEM instance should start up again, and this time with a command window available
to view details of the startup. In addition, the GUI window will also be available for you to
shut down AEM.

ADOBE COPYRIGHT PROTECTED


12. Instead of using the GUI to stop your AEM author instance, use the command window. For
Windows, type CTRL+C in the command window to stop your AEM author instance.

Extend and Customize Adobe Experience Manager 25


(Optional) Task 4: Start AEM using nosamplecontent and change the admin password

In this task, you will install AEM without the We.Retail site using the nosamplecontent run mode
and change the admin password.
To begin installation:
1. Create a separate sibling directory called nosamplecontent on your machine in the same
location as your author and publish directories.

2. In Windows (and also if you are using ReadyTech), this would be:
C:\adobe\AEM\nosamplecontent

3. Copy the quickstart *.jar file and the license.properties file from your author directory and

ADOBE COPYRIGHT PROTECTED


paste it into this new directory.

4. Rename your quickstart file to aem-author-4504.jar.

5. Open a command prompt in the directory where your quickstart *.jar file is copied.

6. In the command prompt, run the following command:

7. java -jar aem-author-4504.jar -r author, nosamplecontent

8. This installs another author instance with the following instructions:

• With the author and nosamplecontent run modes (that is, an author environment
without the We.Retail sample site)

• Running on port 4504 (as specified by the filename you changed)

9. As this is the first time you are installing an instance of AEM using the command line, a
prompt should appear asking you to enter the admin password:

10. Enter a password of your choice for the admin user.

Extend and Customize Adobe Experience Manager 26


11. Re-enter the password to confirm it. Then, wait for your new author instance to install and
start.

Tip: As you did not use the -gui option, no startup window will appear on your
computer to show the progress as AEM installs and loads. Therefore, you will
need to wait for your new browser tab localhost:4504 to appear and signal that
AEM has started.

12. After the initial installation and startup of AEM, enter your new admin password (instead of
admin/admin) to confirm that you can log in and that the We.Retail site does not appear:

ADOBE COPYRIGHT PROTECTED


Note: If you did not initially install AEM using the command line, but used the *.jar file, the
admin password is set for you. The default sign in using this method is:
• User: admin
• Password: admin

13. To confirm that We.Retail site does not appear in the AEM navigation, click Sites on the
default Navigation page. Notice that We.Retail does not appear when you click it (it should
only show options such as Screens, Campaigns and Community Sites):

Extend and Customize Adobe Experience Manager 27


ADOBE COPYRIGHT PROTECTED
14. In your command window, type CTRL+C to shut down your additional AEM author instance
on port 4504 (applicable for Windows only). Be patient as this may take up to 1 or 2 minutes.

Note: You need to use this method to stop AEM because you are not using the -gui parameter.
Therefore, the GUI window is not available for you to stop AEM.

15. Start your author instance on port 4502 again, either using the JAR file method described in
Task 1.1 or using the command line method described in Task 1.3.

Extend and Customize Adobe Experience Manager 28


Module 3: Authoring Basics
As you learned in the previous section, the author instance allows you to create, update and review
content before publishing it. These authoring functions are made available to you through the
AEM UI.
AEM UI: Key Features

The AEM UI combines the advantages of a web interface with the fluidity and responsiveness that
is usually associated with desktop applications. It is touch-optimized for authoring across desktop
and mobile devices.

ADOBE COPYRIGHT PROTECTED


The following table compares the touch and mouse actions, which you can use in AEM:

Device UI Desktop UI
Actions Actions
Tap Click
Touch-and-hold Double-click
Swipe Hover

When you start your author instance and sign in to AEM, you are presented with a start screen that
includes the header bar and the content area.

The content area, by default, displays all applications and capabilities of AEM, such as Projects,
Sites, Experience Fragments and Assets.

Extend and Customize Adobe Experience Manager 29


The header bar displays the default options as shown below and changes depending on the
process or item you have selected:

ADOBE COPYRIGHT PROTECTED


Navigation and Tools

There are two main sections in the AEM UI: Navigation and Tools.

• Navigation
The Navigation section is represented by a compass icon in the AEM UI. By default, when you first
sign in, the Navigation section loads in the content area. All applications of AEM, such as Projects,
Sites, Experience Fragments, Assets, and Forms, are available in this section. This is also the main
section relevant to AEM authors to manage and build content for AEM Sites or leverage assets.

Extend and Customize Adobe Experience Manager 30


Note: The applications within the Navigation section are arranged like folders in a hierarchy.
The applications indicated with a carat symbol, as shown in the following diagram, have child
sub-folders available, such as Assets > Files:

ADOBE COPYRIGHT PROTECTED


• Tools
The Tools section is represented by a hammer icon in the AEM UI. This section displays all AEM
administrative consoles, developer tools, and other technical consoles available. AEM developers
and administrators use this section to develop and administer websites, digital assets, and other
aspects of the content repository.

Extend and Customize Adobe Experience Manager 31


AEM Sites Pages
A page in AEM is similar to a webpage and contains components such as text, images and videos.
These pages are created from templates that define the structure of the page—including where
each component has to be placed.
Creating Pages

To create a page in AEM Sites, you need to follow a simple wizard where the page template is
specified, and the page is given a title and a name. The title is displayed to the user and the name is
used to generate the page URL (and can be derived from the title).

ADOBE COPYRIGHT PROTECTED


Editing Pages

After you create a page, you can edit and add content to it. You can add content by draging the
components available in the side panel onto your page in the page editor.
AEM Keyboard shortcuts

When you select an object in AEM such as a page, the header bar changes to show a menu of
options to work with the object. To aid in editing and managing pages, there are a collection of
keyboard shortcuts you can use to quickly access common tasks and functions, as shown below.
By default, each user’s keyboard shortcuts are enabled.

Extend and Customize Adobe Experience Manager 32


You can customize or enable/disable shortcuts by navigating to Help > Keyboard Shortcuts:

ADOBE COPYRIGHT PROTECTED


Note: For accessibility reasons, shortcuts may be disabled if they conflict with screen readers.

Reference Site: We.Retail

We.Retail is an example retail outdoor equipment reference site that comes with AEM. The
purpose of We.Retail is to show the recommended way of setting up an online presence with AEM
Sites. This site is built with the following best practices of AEM:
• Localized site structure, with language masters live-copied into country-specific sites

• Content fragments

• Core components

• Responsive layout for all pages

• All editable templates

• HTML Template Language (HTL) for all components

• eCommerce capabilities with product catalog

• AEM Communities sites for site visitors

Note: While We.Retail illustrates a retail vertical, only the product catalog and cart features are retail
specific. The site is set up in a way that it can be applied to any vertical.

Extend and Customize Adobe Experience Manager 33


Exercise 1: Create and edit a page in AEM
Scenario: As an author, you need create and edit a page in AEM Sites under the built-in We.Retail
site hierarchy.
Task 1: Create a page

In this task, you will create a demo page using the content template available in AEM.
To create a new page:
1. Ensure you have started and logged on to your AEM author instance on port 4502.

2. The Navigation page is displayed by default in the content area. Click Sites as shown below:

ADOBE COPYRIGHT PROTECTED


3. The column view is displayed.

Note: You may see the Product Navigation tutorial dialog guiding you through the navigation.
You may click Next to proceed through the tutorial and learn the basic AEM UI elements and
navigation, or you may click Close to hide the tutorial.

Extend and Customize Adobe Experience Manager 34


4. In the column view, click the right-pointing arrow next to We.Retail as shown:

ADOBE COPYRIGHT PROTECTED


Tip: If you see a check mark on the We.Retail thumbnail as shown below, it means you have
selected We.Retail for editing and/or managing the page. Click the thumbnail again to clear the
selection and click the right-pointing arrow instead.

5. Click Create > Page as shown:

6. After you click Page, a wizard appears where you need to select a template for your page.
Extend and Customize Adobe Experience Manager 35
7. Click the Content Page template to select it, and click Next as shown:

8. In the Properties step of the page creation wizard, enter the following values for the

ADOBE COPYRIGHT PROTECTED


corresponding fields:

• Title: Demo Page

• Name: demoPage

9. Click Create in the top-right corner to create the page. A Success dialog box is displayed
with a message that your page has been created.

Extend and Customize Adobe Experience Manager 36


10. Click Done. The new page appears as a child page of We.Retail. The page name (Demo
Page) appears in the column view. You can also see the page title (demoPage) below the
page name.

ADOBE COPYRIGHT PROTECTED


11. You can create subpages of any page using the same process.

Extend and Customize Adobe Experience Manager 37


Task 2: Edit a page

In this task, you will edit the page you just created by adding text and image components.
To edit the page:
1. Click the Demo Page (thumbnail) you just created to select it, and click Edit from the
actions bar. Be patient as it may take a few minutes to load the AEM page editor the first
time.

ADOBE COPYRIGHT PROTECTED


Note: You will notice the shortcut keys in the header bar. After you have selected a page,
you may use the keyboard shortcuts such as e to edit the page, p to view page properties,
and Ctrl+c to copy the page.

Extend and Customize Adobe Experience Manager 38


2. When you first open the page in edit mode, you will most likely see a dialog box guiding you
through the different modes. If you want to come back to this tutorial later to learn more
about the AEM editor, clear the Don’t show this again checkbox and then click Close as
shown:

ADOBE COPYRIGHT PROTECTED


3. Ensure the page is open in edit mode by checking in top-right corner to see the Edit mode
highlighted:

Extend and Customize Adobe Experience Manager 39


To add a text component to the page:

4. Click the Toggle Side Panel icon in the top-left corner as shown:

ADOBE COPYRIGHT PROTECTED


5. The side panel is displayed.

6. Click the Components icon as shown:

7. In the Filter field, enter text to search for the Text component, and press Enter. The search
yields results that contain the word ‘text’.

8. Drag the Text (We.Retail) component into the Drag components here box as shown:

Extend and Customize Adobe Experience Manager 40


Your editor should look like the following:

ADOBE COPYRIGHT PROTECTED


Tip: If the ordering is not correct, just drag the text component again to the
Drag components here box.

9. Click the Text component, and then click Edit (pencil icon) from the component toolbar as
shown. A text editor opens in the same tab.

10. Add sample text of your choice in the text editing form that appears, and click Done
(checkmark icon) to save the changes. The text is added to the page.

12. You have now added the first component to your page!

Extend and Customize Adobe Experience Manager 41


To add the image component to your page:
11. In the side panel, click x to clear the search as shown:

ADOBE COPYRIGHT PROTECTED


12. Scroll down and drag the Image (We.Retail) component onto the Drag components here
box as shown:

Extend and Customize Adobe Experience Manager 42


13. Click the Assets icon in the side panel as shown:

ADOBE COPYRIGHT PROTECTED


14. Drag any image from the Assets tab on the Image component. AEM will add the image to
the page.

15. Click Preview in the top-right corner to preview the changes to your page:

16. Press CTRL+SHIFT+M to go back to the edit mode. You may use this method to toggle
between Preview and Edit mode, as you may need to switch often when adding and testing
page content.
Note that this keyboard shortcut is AEM specific and does not depend on the operating
system or the browser. In other words, this shortcut is universal to AEM.

Extend and Customize Adobe Experience Manager 43


17. The only variation to this is the use of ⌘. The key ⌘ is specific to macOS, and is equivalent
to CTRL in Windows.

Note: At this time, your page has been edited, but is not yet live. In order to push changes to
content to the web, your page has to be published (or activated) to a publish instance of AEM.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 44


Module 4: Developer Tools
The three most common developer tools available in AEM for developers and administrators are:
1. CRXDE Lite

2. Web Console

3. Package Manager

CRXDE Lite

ADOBE COPYRIGHT PROTECTED


CRXDE Lite is embedded into AEM and allows you to perform common development and
administration tasks within the browser. It is a light Integrated Development Environment (IDE) for
quick access to the JCR. Because it is embedded in the server and is always available, CRXDE Lite is
often the preferred tool for administrators and developers for working with nodes and properties in
the JCR. It gives quick and direct access to the repository for monitoring, configuration, and

Note: CRXDE Lite is primarily used by front-end developers who develop components and client
libraries in AEM. DE in CRXDE stands for Development Environment.

development.

You can access CRXDE Lite directly by navigating to http://localhost:4502/crx/de/index.jsp or by


navigating to Tools > CRXDE Lite within AEM.
With CRXDE Lite, you can create a project, create and edit files (like .jsp and .java), folders,
templates, components, dialogs, nodes, properties, and bundles. CRXDE Lite is recommended for
most repository-level administration tasks, as well as many light weight development tasks.
You can use CRXDE Lite for:
• Code/content validation

• Product training

• Operations debugging

• Overlays from /libs

Extend and Customize Adobe Experience Manager 45


The CRXDE Lite UI contains:
• Top switcher bar: Enables you to quickly switch between CRXDE Lite, Package Manager, and
Package Share

• Explorer pane: Displays a folder tree structure of all the nodes in the repository

You can perform the following actions on a node in the tree:

 Select the node and view its properties in the Properties tab. Examine all the JCR
properties of different nodes.

 Right-click the node and perform an action on it, such as renaming the node, creating
a new node, creating a folder, and creating a file.

ADOBE COPYRIGHT PROTECTED


• Edit pane: Allows you to edit the code. Double-click a file, such as a .jsp or a .html file, in the
Explorer pane to display its content. You can then modify the code and save the changes.

• Properties tab: Displays the properties of the node that you selected. You can add new properties
or delete existing ones.

Extend and Customize Adobe Experience Manager 46


Web Console
The Web Console in AEM is based on the Apache Felix Web Management Console, and is used to
manage OSGi bundles and configurations. Any changes made through this console are
automatically applied to the running system, without the need to restart the instance.

You can access the console at http://localhost:4502/system/console or by navigating to Tools >


Operations > Web Console within AEM.

Note: If you use the navigation in AEM, the Web Console and CRXDE Lite always open in a new tab in

ADOBE COPYRIGHT PROTECTED


your browser.

The Web Console contains a selection of tabs for maintaining OSGi bundles. The most important
menu selections under the OSGi tab are:
• Bundles- Used for installing and managing bundles.

• Components- Used for managing and controlling the status of components required for AEM.

• Configuration- Used for configuring OSGi bundle, and is the underlying mechanism for
configuring AEM parameters.

Package Management in AEM

A package is a *.zip file that holds AEM repository content in the form of a file-system serialization
called Vault serialization. Vault is provided by Apache Jackrabbit.
Packages provide an easy-to-use-and-edit representation of files, such as pages, assets, and folders.
They also enable you to import and export repository content from one instance or environment
to another.
A package can contain:
• Page-related content

• Project-related content

• Assets-related content
Extend and Customize Adobe Experience Manager 47
• Vault meta information, such as filter definitions and import configuration information

• Other package information such as package settings, package filters, package screenshots, and
package icons

You can use packages for any of the following:


• Install new functionality

• Transfer content between instances

• Back up repository content

• Export content to the local file system

ADOBE COPYRIGHT PROTECTED


You can work with packages by using Package Manager or Package Share. You can access these
options from the AEM UI by navigating to Tools > Deployment > Packages/ Package Share as
shown, or through the following link: http://localhost:4502/crx/packmgr/index.jsp:

Package Manager

Package Manager is used to import or export content on your instance, transfer content between
instances, and back-up repository content. With Package Manager, you can perform the following
common tasks:
• Create, build, and download content packages

• Upload, validate, and install packages

• Modify existing packages

• View package information

You can also use filters to create a package containing page content or project-related content.

Extend and Customize Adobe Experience Manager 48


Creating and Building New Packages

When creating packages using Package Manager, you can apply rules and filters to determine the
content a package should extract from the repository. After you define the content, you can build it.
A package is created in a .zip file and you can download it to your local file system. You can test the
contents of the package before building it.
There are many options in Package Manager to work with packages, such as:
• Rebuild: Helps rebuild the package if there is a change in the repository content.

• Edit: Helps edit filters or rules applied to the package.

• Test: Helps perform a dry run of the installation.

ADOBE COPYRIGHT PROTECTED


• Rewrap: Helps recreate the package with additional information such as thumbnails and icons.

Downloading Packages to the File System

You can download a package by clicking the download link. This link is displayed when the
package details are expanded. After downloading the package, you can unzip the contents of the
package to your local system.
Typically, an unzipped (extracted) content package contains the following folders:
• jcr_root: Contains files and folders that are serialized nodes and properties from the JCR.

• META-INF: Contains metadata regarding node definitions and the filter.xml file that gives
directions to Vault about the paths to include.

Package Share

Package Share is a centralized server where public packages are made available. These packages
may include hotfixes, new functionality, updates or documentation. You can search, download, and
install any package either to your instance or to your local file system.
Within the Package Share, you have access to the following:
• AEM packages provided by Adobe (For example, new functionalities such as updated core
components, hotfixes and service packs)

• Shared packages provided by other organizations and made public by Adobe

You can access this console through the following link:


http://localhost:4502/crx/packageshare/index.html
You can also directly access Package Share (without using CRXDE Lite) through the following link
(after signing in using your Adobe ID):
https://www.adobeaemcloud.com/content/packageshare.html

Extend and Customize Adobe Experience Manager 49


Exercise 1: Install, create, build, and download a package
Scenario: As an AEM developer or administrator/development operations, you need to own all
package management tasks including validating, installing, creating, building and downloading
packages in AEM.
Task 1: Install a package

In this task, you will validate and install a package using the Package Manager.
To validate a package:
1. Ensure that you are logged on to your AEM author instance on port 4502

ADOBE COPYRIGHT PROTECTED


(http://localhost:4502).

2. Navigate to Tools > CRXDE Lite in your AEM author instance as shown:

3. The CRXDE Lite console loads in a new browser tab.

Tip: Bookmark the CRXDE Lite URL (http://localhost:4502/crx/de/index.jsp) in


your browser to access this tool, as you will use it often in your training as well
as in your role as an AEM developer or administrator.

Extend and Customize Adobe Experience Manager 50


4. In the CRXDE Lite console, click the Package icon as shown:

5. This will take you to the AEM Package Manager tool.

Tip: Bookmark the Package Manager URL

ADOBE COPYRIGHT PROTECTED


(http://localhost:4502/crx/packmgr/index.jsp) in your browser in addition to CRXDE
Lite.
6. Click Upload Package.

7. In the Upload Package dialog box, click Browse and select the SamplePackage.zip package
from the Exercise_Files (in the \Module 4 – Package Manager folder) provided to you.

Extend and Customize Adobe Experience Manager 51


8. Click Open, and then click OK as shown:

9. Your uploaded package is now available in AEM Package Manager as shown:

ADOBE COPYRIGHT PROTECTED


10. Click More > Validate as shown:

Extend and Customize Adobe Experience Manager 52


11. Leave all options selected and click Validate in the dialog box. In the Activity Log below,
notice there are no issues with your package:

ADOBE COPYRIGHT PROTECTED


Note: As a best practice, you should validate all packages before installing to check for any
warnings. While this validation tool is covered in depth in other training courses, this utility will
notify you of any issues that impact overlaid JCR resources in /apps, any unsatisfied bundles,
and any ACL (Access Control Lists) conflicts. In other words, this will prevent any problems with
OSGi bundles that are unable to start after installing a package, any impacts on permissions
(ACLs), and files in /libs that impact overlaid files in /apps.

To install your package:


12. Click Install as shown:

13. The Install Package dialog box appears.

14. Ignore the Advanced Settings area and click Install.

15. Check the Activity Log. You can see the content was added from the package. In the activity
log, note that the contents of the package consist of a set of image assets being added to
/content/dam/assets-from-sample-package in the JCR repository.

16. Click the Develop icon as shown:

Extend and Customize Adobe Experience Manager 53


17. This takes you back to CRXDE Lite.

18. Navigate to the /content/dam/assets-from-sample-package folder in the Explorer pane


(at left). The package contents have been added to the JCR in this location as shown:

ADOBE COPYRIGHT PROTECTED


19. Click one of the nodes (such as Superior-Striped-Rock.jpg). Note that in the Properties tab,
properties of that node are shown. In addition, each file has a child jcr:content node. So, at a
basic level, all content in the JCR consists of nodes and properties.

20. In your other browser tab, navigate back to the AEM start (http://localhost:4502).

21. Click the Navigation icon, then click Assets as shown:


Extend and Customize Adobe Experience Manager 54
ADOBE COPYRIGHT PROTECTED
22. Click the Files folder. You can now view the set of images in the Assets from Sample
Package folder that were installed through the package.

Note: You may see a Product Navigation tutorial dialog guiding you through the navigation.
You may click Next to proceed through the tutorial and learn the basic AEM user interface
elements and navigation, or you may click Close to hide the tutorial.

Extend and Customize Adobe Experience Manager 55


Task 2: Create, build, and download a package

In this task, you will use the Package Manager to package We.Retail content for distribution to
another environment.
To create a package:
1. In your CRXDE Lite browser tab, click the Package Manager icon as shown:

ADOBE COPYRIGHT PROTECTED


2. The Package Manager screen appears.

3. Click Create Package as shown:

4. In the New Package dialog box, enter the following details:

• Package Name: We.Retail

• Version: 1.0

• Group: training

5. Click OK. The We.Retail-1.0.zip package is created.

Extend and Customize Adobe Experience Manager 56


6. Click Edit on the newly created package as shown:

ADOBE COPYRIGHT PROTECTED


7. To add filters to the package, click the Filters tab, and click Add Filter.

Note: Filters are the mechanism used to add content to packages. Here, you specify the paths
that contain the content from the JCR you want to include in a package. Before adding filters,
you package is completely empty. You may also restrict filetypes added to a package using
filter rules, such as excluding all *.txt files.

8. For the Root path, browse and select the weretail project folder under apps, and click OK.

Extend and Customize Adobe Experience Manager 57


9. Click Done, and then click Save, as shown:

ADOBE COPYRIGHT PROTECTED


10. Click Build to build the package as shown:

Note: As the package is being built, the activity log is running at bottom, showing a series of
“A” actions in the log. “A” denotes a node is being added to the content package.

11. Click Build again in the confirmation dialog box. The package is now ready for download.

12. Click the Download link to download a copy of the package to your computer. The package
is download to your computer’s default download folder for the browser.

Extend and Customize Adobe Experience Manager 58


Note: You should now see a list of all packages in your instance organized by group on the
left-hand menu. Note that there are now two packages in the training group – the first
package that you installed in Task 3.1 and the second package you just created for We.Retail.
Therefore, you may use the group name to categorize and organize your packages in AEM.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 59


References
AEM 6.4 Help Content (main page): https://helpx.adobe.com/support/experience-manager/6-
4.html
Installation:
• Official list of supported platforms for AEM: https://helpx.adobe.com/experience-manager/6-
4/sites/deploying/using/technical-requirements.html

• Local Machine Operating System Requirements for AEM: https://helpx.adobe.com/experience-


manager/6-4/sites/deploying/using/deploy.html#GettingStarted

Authoring:

ADOBE COPYRIGHT PROTECTED


• Authoring Pages: https://helpx.adobe.com/experience-manager/6-4/sites/authoring/using/page-
authoring.html

• You can download the latest release of the We.Retail site using the following link:
https://github.com/Adobe-Marketing-Cloud/aem-sample-we-retail/releases

• Granite UI (based on Coral 3): https://helpx.adobe.com/experience-manager/6-


4/sites/developing/using/touch-ui-concepts.html

Developer Tools:
• Package Share Direct URL (No need to access through CRXDE Lite):
https://www.adobeaemcloud.com/content/packageshare.html

• Bookmark/Favorite these sites for a local author installation (development environment):

 CRXDE Lite: http://localhost:4502/crx/de/index.jsp

 Web Console: http://localhost:4502/system/console

Package Manager: http://localhost:4502/crx/packmgr/index.jsp

Extend and Customize Adobe Experience Manager 60


Module 5: Configuring AEM Development
Environment

Introduction

You can use Maven and Eclipse to configure Adobe Experience Manager (AEM) development
environment. Maven builds and deploys bundles to Java Content Repository (JCR), and Eclipse helps

ADOBE COPYRIGHT PROTECTED


developers edit code, build Open Services Gateway Initiative (OSGi) bundles, and perform unit tests. These
third-party tools help a developer to comprehend the development effort easily and accurately.

Maven is the recommended build management tool for AEM projects. The following steps are required to
develop an AEM project in Eclipse:

• Install Eclipse

• Install Eclipse AEM plugin

• Set up your AEM project based on Maven

• Build and deploy your AEM project

Objectives

• Describe Maven

• Install and configure Eclipse

• Install and configure the Eclipse AEM plugin

• Set up AEM for Eclipse

• Build and deploy an AEM project

Extend and Customize Adobe Experience Manager 61


Working with Maven
Maven helps build and manage Java-based projects. Maven specifies how software is built and
describes the dependencies of the software. Maven has a central repository from which Maven can
dynamically download Java libraries and plugins and store them in a local cache. This cache can
also be updated by the developers with artifacts created by local projects. Public repositories can
also be updated.
Maven projects are configured using a Project Object Model (POM) stored in a pom.xml file.
Building your AEM project based on Maven offers you the following benefits:
• Integration Development Environment (IDE)-agnostic development environment

ADOBE COPYRIGHT PROTECTED


• Usage of Maven Archetype and Artifacts provided by Adobe

• Usage of Apache Sling and Apache Felix tool sets for Maven-based development setups

• Ease of project import into an IDE

• Easy integration with Continuous Integration Systems

Contents of a POM File

POM files are XML files that contain the identity and the structure of the project, build
configuration, and other dependencies. When executing a task or goal, Maven looks for the POM in
the current directory. Maven reads the POM, gets the needed configuration information, then
executes the goal. A typical POM file includes:
• General project information: This section includes the project name, website URL, and the
organization name. It can also include a list of developers, contributors along with the license
for a project.

• Build settings: This section includes the directory settings of source and tests, plugins, plugin
goals, and site-generation parameters.

• Build environment: This section includes the profiles that can be used in different
environments. The build environment customizes the build settings for a specific environment
and is often supplemented by a custom settings.xml file in the Maven repository. A Maven
repository is a directory that stores all project files, including JAR files, plugins, artifacts, and
other dependencies.

Extend and Customize Adobe Experience Manager 62


• POM relationships or dependencies: This section includes the interdependencies between
projects and their POM settings inherited from parent POMs. These interdependencies
include:

o GroupId

o ArtifactId

o Version

o Dependencies

Super POM

ADOBE COPYRIGHT PROTECTED


A Super POM is:
• Extension of Maven project PMOs

• Stored in ${M2 _ HOME}/lib/maven-xxx-uber.jar in a file named pom-4.0.0.xml in the


org.apache.maven.project package

• Part of the Maven installation

The default Super POM defines a single remote Maven repository with an ID of central. This is the
central repository that all Maven clients are configured to read from by default. A custom
settings.xml file can override this setting.
• Snapshot versions: This section includes snapshot versions used for projects under active
development. Snapshot versions are indicated by the string ‘-SNAPSHOT’. If a project depends
on a software component that is under active development, you can depend on a snapshot
release and Maven will periodically attempt to download the latest snapshot from the
repository when you run a build.

• Property references: This section includes the properties from another part of the POM file,
Java system properties, or implicit environment variables. It supports three environment
variables:

o env

o project

o settings

• Plugins: This section includes plugins that provide functionalities, such as compiling source,
packaging a WAR file, or running JUnit tests. These plugins are retrieved from the Maven
repository. If a new functionality is added to a plugin, you can use it by updating the version
number of the plugin in a single POM configuration file.

Extend and Customize Adobe Experience Manager 63


UberJar

UberJar is a special JAR file provided by Adobe to reduce the number of dependencies. Earlier, for
every API being used, a separate and individual dependency had to be added to the project.
The UberJar file contains:
• All public Java APIs exposed by AEM

• Limited external libraries—all public APIs available in AEM, which comes from Apache Sling,
Apache Jackrabbit, Apache Lucene, Google Guava, and two libraries used for image processing

• The interfaces and classes exported by an OSGi bundle in AEM

• A MANIFEST.MF file with the correct package export versions for all exported packages

ADOBE COPYRIGHT PROTECTED


Using the UberJar File

To use the UberJar file, you must add the following elements to the pom.xml file:
• Dependency element: To add the actual dependency to your project

a. <dependency>

b. <groupId>com.adobe.aem</groupId>

c. <artifactId>uber-jar</artifactId>

d. <version>6.4.0</version>

e. <classifier>apis</classifier>

f. <scope>provided</scope>

g. </dependency>

• Repository element

1. <repositories>

2. <repository>

3. <id>adobe-public-releases</id>

4. <name>Adobe Public Repository</name>

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

6. <layout>default</layout>

7. </repository>

8. </repositories>
Extend and Customize Adobe Experience Manager 64
9. <pluginRepositories>

10. <pluginRepository>

11. <id>adobe-public-releases</id>

12. <name>Adobe Public Repository</name>

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

14. <layout>default</layout>

15. </pluginRepository>

16. </pluginRepositories>

ADOBE COPYRIGHT PROTECTED


If you already use a Maven Repository Manager, such as Sonatype Nexus, Apache Archiva, or JFrog
Artifactory, add the appropriate configuration to your project to reference the Maven repository
manager that you are using and add Adobe’s Maven Repository to your repository manager.

Extend and Customize Adobe Experience Manager 65


Installing and Configuring Eclipse
Eclipse is an open source Integrated Development Environment (IDE) used to edit your project
source locally on your file system. For AEM projects, you need to install Eclipse and the following
plugins:
• Maven Integration for Eclipse (M2E)

• AEM plug-in for Eclipse

Setting up the AEM Plug-in for Eclipse

AEM plug-in has the following benefits:


• Synchronizes both content and OSGi bundles

ADOBE COPYRIGHT PROTECTED


• Supports debugging and code hot-swapping

• Includes a project creation wizard to simplify bootstrapping of AEM projects

• Integrates with AEM instances seamlessly through Eclipse Server Connector

• Easy JCR properties edition

Configuring the AEM Perspective

The AEM perspective offers complete control over all your AEM projects and instances.

Extend and Customize Adobe Experience Manager 66


Using AEM Perspective, you can configure an AEM Server to which Eclipse will connect.

ADOBE COPYRIGHT PROTECTED


The AEM perspective enables you to add and modify nodes and properties in your AEM project
through the AEM Console and the JCR properties view.

Extend and Customize Adobe Experience Manager 67


Exercise 1: Install and configure Eclipse (Optional)
In this exercise, you will install and configure Eclipse.
This exercise includes two tasks:
1. Install Eclipse
2. Configure Eclipse

NOTE: You can skip this exercise if you use a ReadyTech environment because AEM is already
installed as part of the image.

Task 1: Install Eclipse

ADOBE COPYRIGHT PROTECTED


1. Extract files from the Distribution_Files. zip file to the destination directory of your choice.

2. Navigate to the directory where you extracted the contents of the Eclipse installation zip file. For
example, navigate to C:\Program Files\Eclipse\ on Windows or Applications/Eclipse on Mac.

3. Double-click eclipse.exe (or eclipse.app) to start Eclipse. The Eclipse Launcher opens, as shown:

Extend and Customize Adobe Experience Manager 68


4. Accept the default workspace and click Launch. The Eclipse Development Environment opens, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 69


Task 2: Configure Eclipse

1. Click the Workbench logo in the upper-right corner, as shown, to close the welcome screen and
go to the main workbench.

ADOBE COPYRIGHT PROTECTED


The Workbench opens, as shown:

Extend and Customize Adobe Experience Manager 70


2. Verify Eclipse’s JRE is set to a JDK by clicking Window > Preferences > Java > Installed JREs.

You should see a path similar to the one given in the following screenshot. Otherwise, you must
provide the correct directory path to your JDK.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 71


Exercise 2: Install and configure the Eclipse AEM plug-in (Optional)
In this exercise, you will install and configure Eclipse AEM plug-in.

NOTE: You can skip this exercise if you use a ReadyTech environment because AEM is already
installed as part of the image.

1. Open the URL https://eclipse.adobe.com/aem/dev-tools/ or use the files provided in USB

ADOBE COPYRIGHT PROTECTED


contents.

Note: There are two ways to install the plug-in:

• Online: You will provide the link to install the plug-in in Eclipse.

• Offline: You will provide the downloaded plug-in in Eclipse.

You will perform the offline method in this exercise.

2. Double-click eclipse.exe (or eclipse.app) to start Eclipse. The Eclipse Development Environment
opens.

3. Select Help > Install New Software, as shown. The Install window opens.

Extend and Customize Adobe Experience Manager 72


4. Click Add. The Add Repository dialog box opens.

5. Click Archive.

6. Navigate to the repository archive and select the zip file (com.adobe.granite.ide.p2update-1.2.0.zip)
provided for the plug-in from Exercise_Files/05_Dev_Environment/.

7. Click Open. The location of the repository is added.

ADOBE COPYRIGHT PROTECTED


8. Click OK. The Available Software window with the items that you want to install opens, as shown:

9. Click Next. The Review Licenses screen opens.

10. Click the I accept the terms of the license agreements radio button and click Finish. The Installing
Software dialog box with a progress bar opens. The installation should take a minute or two.

Extend and Customize Adobe Experience Manager 73


11. If a Security Warning window pops up, as shown, click OK to continue the installation. The Installing
Software dialog box will re-open until the installation is completed.

12. Click Yes to restart Eclipse to load the newly installed tools.

ADOBE COPYRIGHT PROTECTED


NOTE: This process takes about a minute. If you see a dialog box that asks if you want to
keep the current location of your Eclipse install, click OK. Eclipse opens.

13. Click Workbench in the upper-right corner again. The Workspace opens.

14. Notice the new AEM perspective available in Eclipse. To check this, click the Open Perspective icon, as
shown:

Extend and Customize Adobe Experience Manager 74


15. Notice how the AEM perspective is at the top, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: Keep Eclipse open for the next exercise.

Extend and Customize Adobe Experience Manager 75


Exercise 3: Create an AEM project
This exercise includes two tasks:
1. Create an AEM project
2. Update the POM files for the project

Task 1: Create an AEM project

1. Use the Eclipse icon to the right on the task bar to open Eclipse. The Eclipse Launcher opens.

2. Click Launch. The eclipse-workspace – Eclipse window opens.

ADOBE COPYRIGHT PROTECTED


3. Click File > New > Project. The New Project window opens, as shown:

4. Click Next.

Extend and Customize Adobe Experience Manager 76


5. If not already preselected, click the Archetype drop-down menu, and select
com.adobe.granite.archetypes : aem=project-archetype:13 then click Next.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 77


6. In the Configure Archetype Properties window, replace the project details with:

• Name: TrainingProject

• Group Id: com.adobe

• Artifact Id: training

ADOBE COPYRIGHT PROTECTED


7. Click Next.

Extend and Customize Adobe Experience Manager 78


8. In the Create new Adobe AEM application dialog, select Don’t deploy on a server then click Finish.

ADOBE COPYRIGHT PROTECTED


9. Verify the project was created.

Extend and Customize Adobe Experience Manager 79


Task 2: Update the POM files for the project

Now that you have a basic project from the archetype, you will be able to update the POM files to
support AEM 6.4 and a few dependencies that you will use later in this course.
You will use a text editor (such as Notepad++) and Eclipse to copy and replace the entire contents
of each POM file with updated content from the Exercise Files for the class.

1. In the Exercise Files locate the folder: /05_ Dev_Environment/POM_Files/. Notice the POM files with
names indicate which project they should be used to update. Copy the contents to the matching POM
files in Eclipse as follows:

ADOBE COPYRIGHT PROTECTED


a. Exercise file pom.xml to Eclipse training (Parent POM) > pom.xml

b. Exercise file core_pom.xml to Eclipse training.core > pom.xml

c. Exercise file it-test_pom.xml to Eclipse training.it.tests > pom.xml

d. Exercise file ui-apps_pom.xml to Eclipse training.ui.apps > pom.xml

e. Exercise file ui-content_pom.xml to Eclipse training.ui.content > pom.xml

2. Save the changes.

NOTE: The updated POM files have the following changes to support our project:

The uber-jar was updated from 6.3 to 6.4, which lets retrieve all the correct dependencies for AEM.
A new dependency for core component development is added, as shown:
Parent POM:

Extend and Customize Adobe Experience Manager 80


Core POM:

• A new dependency for Google Gson for json objects is added, as shown:

ADOBE COPYRIGHT PROTECTED


Parent POM:

Core POM:

3. In Project Explorer, right-click training and select Maven > Update Project. The Update Maven
Project window opens.

4. Verify your codebase is selected as training.

5. Select the Force Update of Snapshots/Releases checkbox, and click OK, as shown. Your project is
now updated.

Extend and Customize Adobe Experience Manager 81


ADOBE COPYRIGHT PROTECTED
6. Ignore any errors. You will fix them later.

Congratulations! You have successfully created an AEM project.

Extend and Customize Adobe Experience Manager 82


Building and Deploying a Project Using Maven
After you complete a part of your AEM project development, you can manually build and deploy your
content package or OSGi bundle.
After you deploy your project, you can check the progress of the command in the Console, which is part
of the Eclipse Workspace.

ADOBE COPYRIGHT PROTECTED


An AEM project has the following modules:
Core: A Java bundle that contains all the core functionalities, such as OSGi services, listeners, schedulers,
and component-related Java code, such as servlets or request filters
Apps: Contains the /apps and /etc parts of the project, such as JS and CSS clientlibs, components,
templates, runmode specific configs, and Hobbes tests
Content: Contains sample content that uses the components from the apps module
Tests: Is a set of Java bundles that contains JUnit tests that are executed on the server-side
Launcher: Contains the code that deploys the tests bundle to the server and triggers the remote JUnit
execution
After your project is set up, you can use the Export to Server and Import to Server options to
synchronize content between Eclipse and the AEM server.

Extend and Customize Adobe Experience Manager 83


Exercise 4: Build and deploy the project to AEM
In this exercise, you will build the project by setting up the Maven profile for each module and deploy
the project to AEM.
This exercise includes the following two tasks:
1. Select Maven Profiles for projects
2. Deploy the project

Task 1: Select Maven Profiles for projects

1. Right-click training.core, and select Maven > Select Maven Profiles. The Maven Profile selection

ADOBE COPYRIGHT PROTECTED


window opens.

2. In the Select Maven profiles window, select autoInstallBundle and adobe-public (auto
activated) from the Available profiles section, and click OK, as shown. The autoInstallBundle for
training.core is activated.

3. Right-click training.it.launcher, and select Maven > Select Maven Profiles. The Maven Profile
Selection screen is displayed.

Extend and Customize Adobe Experience Manager 84


4. Select integrationTests and adobe-public (auto activated) from the Available profiles section, and
click OK, as shown. The integration tests for training.ui.launcher is activated.

ADOBE COPYRIGHT PROTECTED


5. Right-click training.ui.apps, and click Maven > Select Maven Profiles. The Maven Profile Selection
screen is displayed.

6. Select autoInstallPackage and adobe-public from the Available profiles section, and click OK. The
autoInstallPackage for the training.ui.content is activated.

You have now set up your Maven profiles.

NOTE: If you see No DS descriptor found at path target/classes/OSGI-


INF/com.adobe.training.core…, it is Eclipse looking for files that you do not need. You can configure
Eclipse to ignore these messages in your own environment.

Extend and Customize Adobe Experience Manager 85


Task 2: Deploy the AEM project

1. Right-click training (parent directory), and select Run As > Maven build... The Edit configuration
and launch dialog box opens.

2. In the Edit configuration and launch dialog box, in the in Goals field, enter clean install and in the
Profiles field, enter: adobe-public, autoInstallPackage, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: You should run the autoInstallPackage profile to ensure you have the correct content
pages built within the repository. If you run autoInstallBundle first, the build will fail to upload
the bundle into Apache Felix.

Extend and Customize Adobe Experience Manager 86


3. Click Apply, and then click Run. The Success message appears:

NOTE: When the AEM project is first run, Maven will download all required dependencies

ADOBE COPYRIGHT PROTECTED


locally to the user’s .m2 directory. Depending on your Internet connection, this can take about 15
minutes. Downloading dependencies is a one-time action if you do not add any new
dependencies. Deploying the project to AEM after the initial deployment only takes a few
seconds.

4. Verify both ui.apps and ui.content content packages are installed in Package Manager and the core
bundle installed in the Web Console.

5. Log in to AEM with the admin credentials.

6. Navigate to Adobe Experience Manager > Tools > CRXDE Lite.

7. Click the Package icon. The installed packages are shown.

8. Verify you see the installed packages, as shown:

Extend and Customize Adobe Experience Manager 87


9. Go to Web Console (http://localhost:4502/system/console/configMgr), and select OSGI > Bundles.

10. Notice that the highest ID is trainingProject.core, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 88


Exercise 5: Configure the AEM Server in Eclipse
The AEM plugin allows you to connect to an AEM server to auto-push changes made in the project
into the JCR. This can be done to synchronize content in the ui.apps and ui.content modules with the
JCR, but also for hot code swap (such as updating the bundle without Maven builds) on changes done
in the core module. The synchronization happens when saving a file in those modules, but can also be
manually triggered (as well as changes done in the repository can be manually imported into the
project).
The AEM server connection allows you to synchronize modules individually, by using the add/remove
resources function.

Task 1: Configure the AEM Server

ADOBE COPYRIGHT PROTECTED


In this task, you will configure a connection to the AEM server to enable content synchronization for
the modules ui.apps and ui.content, but NOT the core module, since you are using Maven to build and
install the code (preferred method).

1. In Eclipse, verify AEM Perspective is selected in the upper-right corner.

2. On the left-hand side of the Eclipse Workspace below Project Explorer, notice the Servers tab. Click
the No Servers are available.

The Define a New Server dialog box opens.

Extend and Customize Adobe Experience Manager 89


3. In the Define a New Server dialog box, select Adobe > Adobe Experience Manager, as shown:

ADOBE COPYRIGHT PROTECTED


4. Accept the default settings for Server’s host name and Server name fields.

5. Click Next. The Add and Remove resources dialog box opens.

6. Select training.ui.apps and training.ui.content one by one, and click the Add button to move the
specified resources from the Available section to the Configured column, as shown:

Extend and Customize Adobe Experience Manager 90


7. Click Finish on the New Server screen. The AEM Server is now defined.

8. On the left-hand side of the workspace (below Project Explorer), select the Servers tab and note
how Adobe Experience Manager at localhost [Stopped] is now available, as shown:

9. To modify the configuration for the AEM Server, double-click the Adobe Experience Manager at

ADOBE COPYRIGHT PROTECTED


localhost [Stopped] to open it in the editor.

10. In the Connection area, change Port to 4502, as shown:

Note: The reason to change the AEM port is that your server is running on 4502.

11. Save the changes (File > Save).

You have now created a connection from Eclipse to the AEM server on your computer

12. Right-click Adobe Experience Manager at locahost on the Servers tab, and click Start. The server is
started.

Extend and Customize Adobe Experience Manager 91


13. Verify the Server has started, as shown:

The contents are now in synch with the AEM server.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 92


Task 2: Export content from Eclipse

1. In Eclipse Project Explorer, navigate to: training.ui.apps > src / main /content/jcr_root > apps>
trainingproject > components > content > helloworld.

2. Double-click helloworld.html. The html page opens.

3. Add the following line at the end of the <pre> tag in the code:

“This is a change in Eclipse”

ADOBE COPYRIGHT PROTECTED


4. Save the changes.

Note: When you save helloworld.html, the AEM Server connection in Eclipse exports
helloworld.html to the JCR.

Extend and Customize Adobe Experience Manager 93


5. In CRXDE Lite, browse to apps > trainingproject > components > content > helloworld >
helloworld.html and double-click helloworld.html. The html page opens.

ADOBE COPYRIGHT PROTECTED


6. Verify the change in CRXDE Lite:

7. Next, we will make a change in CRXDE Lite and import it back into our Eclipse Project. In CRXDE Lite
and navigate to apps > trainingproject > components > content > helloworld.

8. Right-click helloworld component node, and select Create > Create File.

9. Enter the name as Test.html.

Extend and Customize Adobe Experience Manager 94


10. Click OK. The file gets created. Save the changes.

NOTE: Ensure to click Save All after every change in CRXDE lite.

11. In Eclipse, under training.ui.apps, select


/src/main/content/jcr_root/apps/trainingproject/components/content/helloworld, right-click and
select Sling > Import from Server.

12. Accept the default settings and click Finish. The selected node and its children are imported from the
server.

ADOBE COPYRIGHT PROTECTED


13. Under training.ui.apps, navigate to /apps/trainingproject/components/content/ helloworld and
verify Test.html appears in your project, as shown:

NOTE: Step 11 is a very typical process in AEM Java development to sync new content from the JCR
to Eclipse. Remember that Eclipse is our master repository locally and anything created in the JCR
that is a part of our project must be pulled back down into Eclipse. This is a very common process
with config nodes, dialog structures, components, and clientlibs.
IMPORTANT! If you create something in CRXDE Lite that you want to keep, you must sync it
back to Eclipse using the process above.

Extend and Customize Adobe Experience Manager 95


References
For more information on the AEM plugin, see https://docs.adobe.com/docs/en/dev-tools/aem-eclipse.html

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 96


Module 6: OSGi Configurations and Run Modes

Introduction

OSGi configurations are used by developers and administrators to manage Adobe Experience Manager
(AEM) instance settings in conjunction with run modes. This course will teach you the use cases and best
practices for creating custom OSGi configurations and run modes.

ADOBE COPYRIGHT PROTECTED


Objectives

By the end of this course, you will be able to:


• Explain different methods of creating OSGi configurations

• Set run modes in the command line when starting or installing AEM

• Explain how OSGi configurations are organized to match run modes

• Create custom OSGi configurations using Java Content Repository (JCR) nodes

• Start AEM with a custom run mode

• Create a custom OSGi configuration node for a custom run mode

Extend and Customize Adobe Experience Manager 97


OSGi Configurations
OSGi is a fundamental element in the technology stack of AEM and supports modular deployment
of bundles. You can stop, install, and start these bundles individually. The interdependencies are
handled automatically. Each OSGi component is contained in one of the deployed bundles, which
helps manage applications easily.
OSGi configurations are system parameters and settings contained in bundles that you can
manage and configure.

Methods to Create OSGi Configurations

ADOBE COPYRIGHT PROTECTED


You can use three methods to create OSGi configurations:
4. Creating configuration nodes in the JCR (Preferred Method)

5. Using the Web Console

6. Using configuration files

Note: The preferred method as per AEM best practices is creating configuration nodes in the JCR.

Creating Configuration Nodes in the JCR

You can define configurations for each run mode by creating specific nodes by following these
guidelines:
• Create nodes under /apps of type sling:OsgiConfig

• Organize folders for your configuration nodes by run mode

Using this method, it is possible to share a configuration among several instances, which is a best
practice in AEM development.
If you modify the configuration data in the repository, the changes are immediately applied to the
relevant OSGi configuration as if the changes were made using the Web Console, with the
appropriate validation and consistency checks. This also applies to the action of copying a
configuration from /libs to /apps.
You should consider these factors before creating configurations:
• Persistent Identity (PID) of the service

• Run modes—Check whether a specific run mode is required. If so, create the folder specific to that
run mode. For example, create a configuration (config) for all run modes, such as config.author for
the author environment and config.publish for the publish environment.
Extend and Customize Adobe Experience Manager 98
• Requirements of configurations or factory configurations

• Parameters—Configure the individual parameters, including any existing parameter definitions


that will need to be recreated

• Existing configurations—Customize existing configurations by copying the required configurations


from the /libs folder to the /apps folder, and then modifying it in /apps. You can search for the
required configurations using the Query tool available in CRXDE Lite.

Using the Web Console

The Web Console (http://localhost:4502/system/console/configMgr) is a built-in AEM


administrative console used to display and manage OSGi configurations. You can configure all

ADOBE COPYRIGHT PROTECTED


bundles through the Configuration tab, which means you can use it to configure AEM system
parameters.
While configurations can be directly and rapidly edited in the Web Console, the configurations
made with this console are applied immediately, require no restart, and apply to the current
instance, regardless of the current run mode, or any subsequent changes to the run mode.
It is recommended not to use this method as the changes made to an instance:
• Persist only on that instance

• Are not attributable to a user

• Are not associated with a run mode

• Are not tied to a change control system

Note: Use this method only for temporary testing in a development environment.

Using Configuration Files

Configuration files are files ending with the extension *.config in the /launchpad directory of your
AEM crx-quickstart directory structure. Although these files can be used to manage OSGi
configurations, it is recommended that you never edit these files to manage OSGi configurations.
This is a risky process and could lead to system instability. If configuration files are accessed, they
should be kept as view-only (or read-only) and only be used for troubleshooting purposes.

Extend and Customize Adobe Experience Manager 99


Configuration Persistence

It is important to note a few details on how changes to the configurations are persisted:
• By default, you can find any change made to a configuration in /apps/system/config.

• The default settings in AEM are saved in *.config files under /crx-quickstart/launchpad/config.

Adding a New Configuration to the Repository

Warning: You must never edit the folders or files under the /crx-
quickstart/launchpad/config folder.

ADOBE COPYRIGHT PROTECTED


When creating configuration nodes, you must ensure their names are equal to the Persistent
Identity (PID) of the configuration in the Web Console. For example, you can navigate to the Web
Console and see these names listed as service.pid properties. These configuration nodes must be
child-nodes of node type sling:folder with a name starting with the config followed with a period.
All the run modes that the config applies to are also separated with a period, such as config.author,
config.publish, config.author.dev and config.author.foo.dev.
To create a configuration node, you need to know the following:
• The Persistent Identity (PID) of the service. You can do this through the Configurations field in the
Web Console. The name is shown in brackets after the bundle name (or in the Configuration
Information towards the bottom of the page).

• Whether a specific run mode is required. Create the following folders based on the run mode:

 config—for all run modes

 config.author—for the author environment

 config.publish—for the publish environment

 config.<run-mode>—as appropriate

• Whether a Configuration or Factory Configuration is necessary

• The individual parameters to be configured; including any existing parameter definitions that will
need to be recreated. Reference the individual parameter field in the Web Console. The name is
shown in brackets for each parameter.

Extend and Customize Adobe Experience Manager 100


• Whether the configuration already exists in /libs. To list all configurations in your instance, use the
Query tool in CRXDE Lite to submit the following SQL query:

select * from sling:OsgiConfig


If so, you can copy this configuration to /apps/<yourProject>/, then customize it in the new
location.
You can create a config node in the repository using CRXDE Lite.
For each parameter you want to configure, create a property on this node.
Changes are applied as soon as the node is updated by restarting the service (as with changes
made in the Web Console).

ADOBE COPYRIGHT PROTECTED


Resolution Order at Startup

The following order of precedence is used when resolving configuration nodes upon AEM startup:
1. Repository nodes under /apps/*/config, either with type sling:OsgiConfig or property files

2. Repository nodes with type sling:OsgiConfig under /libs/*/config

3. Any config files from <cq-installation-dir>/crx-quickstart/launchpad/config/ on the local file


system

This means project-specific configurations under /apps take precedence over /libs.
Resolution Order at Runtime

Configuration changes made while AEM is running triggers a reload with the modified
configuration. The following order of precedence applies:
1. Modifications made in the Web Console

2. Modifications made in /apps

3. Modifications made in /libs

Extend and Customize Adobe Experience Manager 101


Resolution of Multiple Run Modes

For run mode-specific configurations, you can combine multiple run modes. For example, you can
create configuration folders in the following style and syntax:
/apps/<your_application>/config.<runmode1>.<runmode2>/
Configurations in these folders will be applied if all run modes match a run mode defined at
startup. For example, if an instance was started with the run modes publish,dev,emea,
configuration nodes in /apps/*/config.emea, /apps/*/config.publish.dev/ and
/apps/*/config.publish.emea.dev/ will be applied, while configuration nodes in
/apps/*/config.publish.asean/ and /config/publish.dev.emea.noldap/ will not be applied.
If multiple configurations for the same PID are applicable, the configuration with the highest

ADOBE COPYRIGHT PROTECTED


number of matching run modes is applied.
For example, if an instance was started with the run modes publish,dev,emea, and both
/apps/*/config.publish/ and /apps/*/config.emea.publish/ define a configuration for
com.day.cq.wcm.core.impl.VersionManagerImpl, the configuration in /apps/*/config.emea.publish/
will be applied.

Extend and Customize Adobe Experience Manager 102


Run Modes
Introduction to Run Modes

You can run your AEM instance for specific purposes by using specific run modes. For example,
author, publish, test, development, and so on. For run modes, you can:
• Define collections of configuration parameters for each run mode. A basic set of configuration
parameters is applied to all run modes; you can then configure additional sets to meet your
specific environment. These are applied as required.

• Define additional bundles to be installed for a specific mode.

ADOBE COPYRIGHT PROTECTED


Custom Run Modes

AEM has built-in author and publish run modes (do not remove these). If required, you can add
additional custom run modes. For example, you can configure run modes for:
• Environment: local, development, test, and production

• Location: Berlin, Basel, Timbuktu

• Company: acme, partner, customer

• Special system type: importer

While you can change run modes after installation, several specific run modes, called installation
run modes, cannot be altered once an AEM instance is installed. These include author/publish and
samplecontent/nosamplecontent. These two pairs of run modes are mutually exclusive
(specifically, AEM can be defined as author or publish, but not both). Author run mode can be
combined with samplecontent and nosamplecontent, but not both at the same time.
Customized run modes can differentiate instances by purpose, stage of development, or location.
Some examples of complex run modes (based on different locations and facilities) are:
• author, development

• publish, test

• author, intranet, us

All settings and definitions are stored in the repository and activated by setting the appropriate run
mode.

Extend and Customize Adobe Experience Manager 103


Setting Run Modes

It is possible to define specific run mode(s) that a specific instance should run on. By default, an
author instance runs on run mode “author” and a publish instance runs on run mode ”publish”. It is
possible to define several run modes for one instance, such as author, foo, and dev. These run
modes can be set as Java Virtual Machine (JVM) options. For example, to set run modes from the
command line:

java -jar cq-quickstart.jar -r author,foo,dev

Alternatively, in the start script (or batch file used to start AEM):

ADOBE COPYRIGHT PROTECTED


::* runmode(s)
set CQ_RUNMODE=author,foo,dev

Or, by adding entries to the crx-quickstart/conf/sling.properties file. For example:

sling.run.modes=author,foo,dev

Note: It is recommended that as a best practice, you define run modes using the command line, or by editing
the start scripts located in /crx-quickstart/bin/start. It is not recommended to use the sling.properties file to
define run modes.

Configurations per Run Mode

To create separate configuration settings per run mode in the JCR, create folder names using the
syntax of config.<runmode> for each run mode used. For example, configurations that apply to the
publish run mode would be set in this folder: /apps/myapp/config.publish
For systems with run modes publish and Berlin: /apps/myapp/config.publish.berlin
Configurations for Different Run Modes

Some examples of configuration settings that may be needed for different run modes are:
• Different mail server configurations per location:

config.basel/com.day.cq.mailer.DefaultMailService
config.berlin/com.day.cq.mailer.DefaultMailService

• Enabling or disabling debugging per environment:

Extend and Customize Adobe Experience Manager 104


config.prod/com.day.cq.wcm.core.impl.WCMDebugFilter
config.dev/com.day.cq.wcm.core.impl.WCMDebugFilter

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 105


Additional Information on Run Modes

When using different configurations for separate run modes, the following apply:
• Partial configurations are not supported

• Configuration with maximum matching run modes wins

To avoid unexpected results:


• Always set all properties to avoid confusion

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 106


Exercise 1: Create custom OSGi configurations and start AEM with a custom
run mode
Scenario: As a developer or system administrator, you need to:
• Follow the best practices by creating OSGi configurations in the JCR using /apps

• Be able to start AEM with a custom run mode using the command line

• Create multiple sets of custom OSGi configurations to match custom run modes in order to specify
different groups of settings for each instance or environment you are deploying

In a typical development process, there are multiple servers designated for specific tasks in your

ADOBE COPYRIGHT PROTECTED


infrastructure. Each server will have different configurations depending on location, type, task, and
purpose. You need to use OSGi configurations and Run Modes to manage these configurations.
Task 1: Create a custom OSGi configuration node

In this task, you will create a custom OSGi configuration node to change the default “root” start
page (http://localhost:4502) that loads when you sign in to AEM from start.html to sites.html.
1. Ensure your AEM author instance is running and you are logged on.

2. Navigate to the Web Console at http://localhost:4502/system/console (or within AEM, use


Tools > Operations > Web Console).

3. Select OSGi > Configuration.

4. To locate the Root Mapping OSGi configuration, search for the words: Day CQ Root
Mapping in your browser.

Extend and Customize Adobe Experience Manager 107


5. Click this configuration to open it in a dialog window:

6. Copy the value for Persistent Identity (PID). You will create a configuration node for this
configuration in CRDXE Lite using this value under apps as per the best practice to change

ADOBE COPYRIGHT PROTECTED


OSGi configurations. Keep this Web Console page open for reference.

7. In a new browser tab, open CRXDE Lite by navigating to http://localhost:4502/crx/de (or


within AEM, use Tools > CRXDE Lite).

8. Navigate to apps > weretail.

9. Right-click the weretail folder and create a new folder node by selecting Create > Create
Folder.

10. Enter config.author in the Name field.


11. Click OK.

12. Click Save All in the top-right corner to ensure your new folder is saved.

13. Right-click the config.author folder node and create a new node by selecting Create >
Create Node.

14. Specify the following details:

 Paste the following PID you copied from the Web Console in the Name field:
com.day.cq.commons.servlets.RootMappingServlet

Extend and Customize Adobe Experience Manager 108


 Select the Type as: sling:OsgiConfig

Tip: Ensure there are no whitespaces or bullet points preceding the Name
value. These may be included in the value you copied previously from the
Web Console, so if they do get pasted in, remove them.

15. Click OK, and thenclick Save All in CRXDE Lite to ensure your new node is saved.

16. With the com.day.cq.commons.servlets.RootMappingServlet node selected, navigate to the


JCR Properties tab of CRXDE Lite and add the following:

ADOBE COPYRIGHT PROTECTED


 Name: rootmapping.target

 Type: String

 Value: /sites.html

Tip: The property value of rootmapping.target can be obtained from the


OSGi configuration in the Web Console. Property names are always in
parentheses at the end of the description of each field. Refer to the screen

17. Click Add in the bottom-right corner in the JCR Properties tab to add your property.

The property is added to the Properties tab as shown:

18. Click Save All in CRXDE Lite to ensure your property is saved.
Extend and Customize Adobe Experience Manager 109
19. Open a new browser tab and navigate to the AEM author instance (http://localhost:4502).
The Sites page should open, as this is what you configured with your custom configuration
node.

ADOBE COPYRIGHT PROTECTED


Note: AEM picks up this configuration because you placed it under config.author and the
instance is using the author run mode currently. In Task 1.2, you will use a tool to check the
current run modes of an instance.

Extend and Customize Adobe Experience Manager 110


Task 2: Start AEM with a custom run mode

In this task, you will start your AEM author instance using a custom run mode called dev via
command-line.
1. In the AEM Web Console (http://localhost:4502/system/console), navigate to Status > Sling
Settings.

2. Observe the current run modes your author instance is using (s7connect, crx3, author,
samplecontent, crx3tar):

ADOBE COPYRIGHT PROTECTED


You will now add another run mode and use this tool at the end of this task to verify
your custom run mode.
3. To shut down your running AEM author instance, you can click the click the ON / OFF
toggle button in the GUI window:

If you are running AEM using the command line, use CTRL+C (in Windows) in your
command window to shut down AEM.

4. Now, you will start AEM using the command line in order to use a custom run mode. This is
because the custom run modes can be generated using command line parameters. Navigate
to the directory on your machine where your author instance quickstart file resides.

Extend and Customize Adobe Experience Manager 111


Tip: If you are using a ReadyTech instance, this directory should be

5. Start AEM again using the following command that specifies the author and dev run modes:

java -jar aem-author-4502.jar -r author,dev -gui

6. Your AEM instance should start up again, this time with a command window available to
view details of the startup. In addition, the GUI window will also be available.

Tip: Be patient as it may take up to two minutes for your instance to start.

ADOBE COPYRIGHT PROTECTED


7. Sign in to AEM again.

8. In the AEM Web Console (http://localhost:4502/system/console), navigate to Status > Sling


Settings (or, refresh that page in your browser tab, as you may already have it open). Your
custom run mode should now appear:

Extend and Customize Adobe Experience Manager 112


Task 3: Create a custom OSGi configuration node for a custom run mode

In this task, you will change the default “root” page to load CRXDE Lite for the custom ‘dev’ run
mode.
To create another root mapping OSGi configuration node:
1. Open CRXDE Lite using http://localhost:4502/crx/de or from AEM by navigating to Tools >
CRXDE Lite.

2. Navigate to apps > weretail.

3. Right-click the weretail folder to create another folder node.

ADOBE COPYRIGHT PROTECTED


4. Select Create > Create Folder.

5. Enter config.author.dev in the Name field:

6. Click OK, and click Save All in CRXDE Lite to ensure your new folder is saved.

7. Now, instead of manually creating another root mapping node that is very similar to the one
you created in Task 1.1, you can copy the node you created and paste it into your new folder
and make the necessary changes.

Copy the com.day.cq.commons.servlets.RootMappingServlet node from the config.author


folder by right-clicking the node and selecting Copy.

8. Right-click the config.author.dev folder and select Paste.

Extend and Customize Adobe Experience Manager 113


9. Click Save All.

Your node structure should now look like this:

ADOBE COPYRIGHT PROTECTED


10. Now, to change the properties of your copied node (config.author.dev), double-click the
Value field for the rootmapping.target property in the Properties tab. This will make the
field editable.

11. Enter /crx/de/index.jsp in the Value field.

12. Click Save All in CRXDE Lite to ensure your property is saved.

13. To verify if the change was made to the dev run mode, when http://localhost:4502 opens in
your browser and you sign in, it should automatically navigate you to the CRXDE Lite page.

Question: Why does CRXDE Lite now load, but not the Sites page? What command could
you use to start AEM with the Sites page?

Extend and Customize Adobe Experience Manager 114


Answers to Questions
Question: Why does CRXDE Lite now load, but not the Sites page? What command could you use
to start AEM with the Sites page?
Answer: CRXDE Lite loads because the root mapping property folder config.author.dev matches
the dev custom run mode you supplied in the command when starting AEM. The Sites page
matches the author run mode, but in this case, the dev run mode takes precedence when the
same PID is used in two or more configuration folder nodes. This is because the configuration
matching the highest number of run modes is used. The command you could use to start AEM
with the Sites page is:

ADOBE COPYRIGHT PROTECTED


java -jar aem-author-4502.jar -r author -gui

Extend and Customize Adobe Experience Manager 115


References
For further information on OSGi Configurations and Run Modes, visit:
• Configuring Run Modes: https://helpx.adobe.com/experience-manager/6-
4/sites/deploying/using/configure-runmodes.html

• Configuration Nodes: https://helpx.adobe.com/experience-manager/6-


4/sites/deploying/using/configuring-osgi.html

• Lists of Common Configurations: https://helpx.adobe.com/experience-manager/6-


4/sites/deploying/using/osgi-configuration-settings.html

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 116


Module 7: Configuring Custom Loggers in AEM

Introduction

Adobe Experience Manager (AEM) log files provide detailed information about the current system
state of AEM. It is important to maintain the AEM system, so that the system runs without any

ADOBE COPYRIGHT PROTECTED


issues. Log files enable you to debug some common issues that you encounter in AEM. In addition
to the default system log files, you can create and customize your own log files. Your customized
log files help you better track messages produced by your own applications and separate them
from the default log entries.
Objectives

• Explain the Logging system in AEM

• Explain Loggers and Writers in AEM

• Configure custom Loggers in AEM

Extend and Customize Adobe Experience Manager 117


AEM Logging System
In AEM, you can configure the following:
Global parameters for the central logging service
Request data logging for request information
Specific settings for the individual services, such as an individual log file and format for the log
messages
The user login process in AEM is based on Apache Sling. The root logger defines the global settings
for the logging system in AEM. The root logger is configured by using Apache Sling Logging
Configuration. The org.apache.slng.commons.log bundle manages the logging system. This bundle
helps:

ADOBE COPYRIGHT PROTECTED


• Implement the OSGi log service specification and register the LogService and LogReader
services
• Export four commonly used APIs:
• Apache Commons Logging
• Simple Logging Façade for Java (SLF4J)
• Log4j
• Java.util.logging
• Configure the logging system by using Logback, which is integrated with the OSGi
environment
• Configure the logging system by using both Logback xml or OSGi
In AEM, you can configure:
• Global logging. This defines the global settings for logging in AEM:
o Logging level
o Central log file location
o Number of versions saved
o Version rotation - either maximum size or a time interval
o Format used when writing log messages
• Easy integration with Continuous Integration Systems

Contents of a POM File

POM files are XML files that contain the identity and the structure of the project, build
configuration, and other dependencies. When executing a task or goal, Maven looks for the POM in

Extend and Customize Adobe Experience Manager 118


the current directory. Maven reads the POM, gets the needed configuration information, then
executes the goal. A typical POM file includes:
Access.log: Registers all access requests sent to AEM and the repository

Types of Log Files in AEM


The types of log files in AEM are:
• Audit.log: Registers all modern actions
• Error.log: Registers all error messages
• Request.log: Registers all access requests along with their responses

ADOBE COPYRIGHT PROTECTED


• Stderr.log: Holds error messages generated during startup
• Upgrade.log: Provides a log of all upgrade operations
These files are available in the installation directory /crx-quickstart/logs.
By default, the error, access, history, and request logs rotate once per day. When this occurs, the
existing log files are appended with a timestamp and a new file is created.
In AEM, you can view the log files through the Web Console at:
http://localhost:4502/system/console/slinglog

Extend and Customize Adobe Experience Manager 119


Loggers and Writers
The logging system in AEM consists of two elements, a Logging Logger and a Logging Writer. The
Logging Logger collects data from different components inside AEM, filters them by requested
severity level, and redirects the output to a configured Logging Writer. The Logging Writer persists
the data provided by the logger to the physical file. For example, the error.log file is created by the
Logging Writer on a rotational basis.
Loggers and Writers for an individual service

ADOBE COPYRIGHT PROTECTED


In addition to the global settings, AEM allows you to configure specific settings for an individual
service:
• Specific logging level

• Individual log file location

• Number of versions to be kept

• Version rotation - either maximum size or the time interval

• Format used when writing log messages

• Logger -the OSGi service supplies log messages

You can channel log messages for a single service into a separate file. AEM uses the following
process to write log messages to a file:
1. The OSGi service (logger) writes a log message.
2. The Logging Logger takes this message and formats it according to your specification.
3. The Logging Writer writes all these messages to the physical file you defined.

Logging Logger and Logging Writer are linked by the following parameters:
Logger (Logging Logger): Defines the service(s) generating the messages.
Log File (Logging Logger): Defines the physical file for storing log messages. This parameter links a
Logging Logger with a Logging Writer.
Log File (Logging Writer): Defines the physical file the log messages will be written to.

Extend and Customize Adobe Experience Manager 120


Standard Loggers and Writers

The following table lists the standard Writers and Loggers available in AEM:

Logger Links to – Logger/Writer


Apache Sling Customizable Request Data Logger Apache Sling Request Logger
(org.apache.sling.engine.impl.log.Request.LoggerS (org.apache.sling.engine.impl.log.Request.Logger)
ervice) Write messages to either request.log or access.log
Writes messages about requests to the request.log
file
Apache Sling Logging Logger configuration Apache Sling Logging Writer Configuration
(org.apache.sling.commons.log.LogManager.factor (Writer)

ADOBE COPYRIGHT PROTECTED


y.config) (org.apache.sling.commons.log.LogManager.facto
Writes information messages to logs/error.log ry.writer)

Apache Sling Logging Logger configuration Does not link to any specific Writer. It creates and
(org.apache.sling.commons.log.LogManager.factor uses an implicit writer with default configuration.
649d51b7-6425-45c9-81e6-2697a03d6be7)
Writes warning messages to logs/error.log for the
service ogr.apache.pdfbox

In the above table, the first logger links to another logger, and the second logger links to the writer.
The third logger does not link to a specific writer, so it creates and uses an implicit writer with
default configuration (daily log rotation).

Extend and Customize Adobe Experience Manager 121


Creating Your Own Loggers and Writers

To define your Logger/Writer pair:

1. Create a new instance of the Factory Configuration Apache Sling Logging Logger Configuration.

a. Specify the log file.

b. Specify the logger.

ADOBE COPYRIGHT PROTECTED


c. Configure the other parameters as required.

2. Create a new instance of the Factory Configuration Apache Sling Logging Writer Configuration.

a. Specify the log file.

b. Configure other parameters as required.

Extend and Customize Adobe Experience Manager 122


Exercise 1: Observe project logger config node
In this exercise, you will observe the custom log file and the logger configuration node that created
it.
This configuration node was created by AEM Archetype.

1. Open the AEM instance folder (C:\adobe\AEM\author), as shown:

ADOBE COPYRIGHT PROTECTED


2. Navigate to crx-quickstart\logs\. The list of log files is shown:

Note: The project-trainingproject.log is the custom log file that was created from the AEM
Archetype for this project. By default, all the log messages for this course are located in this log
file.

3. Launch Eclipse by double-clicking the shortcut for Eclipse on your desktop and open your
workspace. The trainingProject opens in the Eclipse Development Environment.

Extend and Customize Adobe Experience Manager 123


4. In Eclipse Project Explorer, navigate to training.ui.apps > src/main/content/jcr_root [nt:folder] > apps
[nt:folder] > trainingproject [nt:folder]/config [nt:folder]. You will see the file
org.apache.sling.commons.log.LogManager.factory.config-trainingproject, as shown:

ADOBE COPYRIGHT PROTECTED


5. Double-click the file org.apache.sling.commons.log.LogManager.factory.config-trainingproject.
The file opens in the Eclipse text editor, as shown:

6. Click the JCR Properties tab below the file. The Properties window opens.

7. Observe the JCR properties for the log, as shown:

8. Log on to AEM with the admin credentials. You are now logged on to the server.

Extend and Customize Adobe Experience Manager 124


9. Click Adobe Experience Manager at the top left and navigate to Tools > General >
CRXDE Lite. The CRXDE Lite screen opens.

10. Browse to apps > trainingproject > config >


org.apache.sling.commons.log.LogManager.factory.config-trainingproject. The
org.apache.sling.commons.log.LogManager.factory.config-trainingproject is
selected,
as shown:

ADOBE COPYRIGHT PROTECTED


11. Double-click org.apache.sling.commons.log.LogManager.factory.config-
trainingproject. The Properties window opens. Observe the values for the log
properties:

12. Open a browser and go to http://localhost:4502/system/console/configMgr. The


Adobe Experience Manager Web Console Configuration page opens, as shown:

Extend and Customize Adobe Experience Manager 125


13. User your browser to search for and find Apache Sling Logging Logger Configuration.
The Apache Sling Logging Logger Configuration is listed, as shown:

ADOBE COPYRIGHT PROTECTED


14. Under Apache Sling Logging Logger Configuration, find the configuration called,
logs/project-trainingproject.log: info.

15. Click Apache Sling Logging Logger Configuration to open it. The logs for Apache
Sling Logging Logger Configuration opens, as shown:

Extend and Customize Adobe Experience Manager 126


16. Expand logs/project-trainingproject.log: info and verify the properties are same as
the configuration node.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 127


Exercise 2: Create custom loggers
In this exercise, you will create a logger and verify the corresponding log file is created.

1. Open the AEM instance folder (C:\adobe\AEM\author), as shown:

ADOBE COPYRIGHT PROTECTED


2. Navigate to crx-quickstart\logs\. The list of log files is shown:

3. Open the Log Support console at http://localhost:4502/system/console/slinglog. The Loggers opens.

Extend and Customize Adobe Experience Manager 128


4. Click Add new Logger, as shown.

ADOBE COPYRIGHT PROTECTED


Notice that a new row got added, as shown:

5. On the Log Level drop-down menu (where INFO is the default), select DEBUG, as shown:

6. Enter the Log File (name) as, logs\LoginTrace.log.

7. Enter the Logger as, org.apache.sling.auth.core.impl.SlingAuthenticator, as shown:

Extend and Customize Adobe Experience Manager 129


8. Click Save. You will see the logger is created successfully, as shown:

9. Navigate to crx-quickstart\logs directory again and see if the LoginTrace.log was


created successfully, as shown:

ADOBE COPYRIGHT PROTECTED


10. Log out from AEM, and then log in again.

Extend and Customize Adobe Experience Manager 130


11. Open the LoginTrace.log file and observe how the log file contains the entries as you
configured them in the logger settings.

ADOBE COPYRIGHT PROTECTED


Note: The log entries ensure the events are captured in the log file.

Extend and Customize Adobe Experience Manager 131


Module 8: Deep Dive into OSGi

Introduction

Adobe Experience Manager (AEM) architecture consists of frameworks such as Open Services Gateway
Initiative (OSGi) and Apache Sling. OSGi defines a dynamic component written in Java. The OSGi
specifications enable a development model where dynamic application comprises reusable components.

ADOBE COPYRIGHT PROTECTED


Objectives

• Explain OSGi architecture

• Implement a bundle activator

• Explain OSGi annotations

• Implement OSGi configurations

Extend and Customize Adobe Experience Manager 132


OSGi Architecture
The OSGi has a layered model, as shown in the following figure:

ADOBE COPYRIGHT PROTECTED


• Bundles: Are the OSGi components made by developers

• Services: Connects bundles in a dynamic way by offering a publish-find-bind model for plain old
Java objects

• Life Cycle: Is the API to install, start, stop, update, and uninstall bundles

• Modules: Defines how a bundle can import and export code

• Security: Handles the security aspects

• Execution Environment: Defines the methods and classes are available in a specific platform

Extend and Customize Adobe Experience Manager 133


Bundles

Bundles are built on Java’s existing standard way of packaging classes and resources together—the
JAR file (.jar). An OSGi bundle is just a JAR file with additional metadata added to the manifest file.
The OSGi metadata is provided as header information in the META- INF/MANIFEST.MF file. The
additional information consists of:
• Bundle name(s):

o A symbolic name used by OSGi to determine the bundle’s unique identity

o An optional, human-readable, descriptive name

• Bundle version

ADOBE COPYRIGHT PROTECTED


• The list of services imported and exported by this bundle

• Optional information, such as the:

o Minimum Java version the bundle requires

o Vendor of the bundle

o Copyright statement

o Contact address

Life Cycle

A life cycle layer adds bundles that can be dynamically installed, started, stopped, updated and
uninstalled. Bundles rely on the module layer for class loading, but add an API to manage the
modules in run time. The life cycle layer introduces dynamics that are generally not part of an
application. Extensive dependency mechanisms are used to assure the correct operation of the
environment. Life cycle operations are fully protected with the security architecture.

Extend and Customize Adobe Experience Manager 134


Dependency Management Resolution in OSGi

A bundle is present in the container.

ADOBE COPYRIGHT PROTECTED


When a new bundle is provided, it is installed into the OSGi container.

The OSGi container resolves the new bundle.


NOTE: The Resolved state of a bundle is the state it can reach after being installed and when all its
required dependencies are satisfied.

The OSGi container provides automatic dependency resolution.

Extend and Customize Adobe Experience Manager 135


Modules

Modularity is at the core of the OSGi specifications and is embodied in the bundle concept.
Modularity is about keeping things local and not sharing. You should be familiar with the term
bundle in Java, which means a JAR file. In Java bundle, the contents of the JAR are completely
visible to all the other JARs. In OSGi bundle, the contents of JAR are hidden unless they are
explicitly exported. If a OSGi bundle wants to use another JAR, it must explicitly import the parts it
needs. Therefore, there is no sharing of the JAR.
Although the code hiding and explicit sharing provides many benefits, such as allowing multiple
versions of the same library being used in a single VM, the code sharing is there only to support
OSGi services model. The OSGi services model is about bundles that collaborate.
Services

ADOBE COPYRIGHT PROTECTED


A bundle can create an object and register it with the OSGi service registry under one or more
interfaces. Other bundles can go to the registry and list all the objects that are registered under a
specific interfaces or class.
A bundle can register a service, get a service, and listen for a service to appear or disappear. Any
number of bundles can register the same service type, and any number of bundles can get the
same service.
This is shown in the following figure:

Each service registration has a set of standard and custom properties. An expressive filter language
is available to select only the services in which you are interested. Properties can be used to find
the proper service or can play other roles at the application level.
Services are dynamic. This means a bundle can decide to withdraw its service from the registry
while other bundles are still using this service. Bundles using such a service must then ensure they
no longer use the service object and drop any references. OSGi applications do not require a
specific start ordering in their bundles.

Extend and Customize Adobe Experience Manager 136


Service Registry Model

OSGi provides a service-oriented component model by using a publish/find/bind mechanism. For


example, using the OSGi Declarative Services, the consuming bundle says, I need X and X is
injected.
Because you cannot depend on any particular listener or a consumer being present in the
container at any particular time, OSGi provides dynamic service look up using the Whiteboard
registry pattern, as shown:

ADOBE COPYRIGHT PROTECTED


Instead of registering a listener/consumer object with the service provider, the consumer creates
an object that implements the OSGi listener interface, providing a method that should be called
when the event of interest occurs. When the desired event occurs, the service provider requests a
list of all the services of the same object type and then calls the action method for each of those
services. The burden of maintaining the relationships between service providers and service
consumers is shifted to the OSGi framework. The advantage of doing this is that the OSGi
framework is aware of the bundle status and the life cycle, and will unregister a bundle’s services
when the bundle stops.

Extend and Customize Adobe Experience Manager 137


Dynamic Service Lookup

The following steps show how the lookup is managed for dynamic services.
1. Examine the existing service.

ADOBE COPYRIGHT PROTECTED


2. Install the new bundle.

3. Activate the new bundle.

Extend and Customize Adobe Experience Manager 138


4. Automatic resolution of package dependency.

ADOBE COPYRIGHT PROTECTED


5. Manual service dependency resolution.

OSGi Service Advantages

In OSGi-based systems, functionality is mainly provided through services. Services implement one
or more interfaces, which define the type of service provided. It is the life cycle of the bundle that
defines the life cycle of the service. A service object may be instantiated when the bundle is started,
and is automatically removed when the bundle is stopped.
The advantages of OSGi services are:
• Lightweight services

• Lookup is based on the interface name

• Direct method invocation

• Good design practice

• Separates the interface from implementation

• Enables reuse, substitutability, loose coupling, and late binding

Extend and Customize Adobe Experience Manager 139


Declarative Services

Declarative services are a part of the OSGi container and simplify the creation of components that
publish and/or reference OSGi Services.
Features of Declarative Services:
• No need to write explicit code to publish or consume services.

• The service implementation class is not loaded or instantiated until the service is requested by a
client.

• Components have their own life cycle, bounded by the life cycle of the bundle, in which they are
defined.

ADOBE COPYRIGHT PROTECTED


• Components can automatically receive configuration data from Configuration Admin.

Components are declared by using XML configuration files contained in the respective bundle, and
listed in the
Service-Component bundle manifest header. Declarative services resolve the bundle through XML
configuration files. You may handwrite and register these configuration files.
Declarative services are a good alternative to:
• Writing an activator

• Registering the bundle in the framework

• Using the service tracker

The OSGi Service Component (Declarative Services) reads the descriptions from started bundles.
The descriptions are in the form of XML files, which define the set of components for a bundle. It is
through the XML configuration definition information that the container:
• Registers the bundle’s services

• Keeps track of the dependencies among bundles

• Starts and stops services

• Invokes the optional activation and deactivation method

• Provides access to bundle configuration

Extend and Customize Adobe Experience Manager 140


Deployment of Bundles

Bundles are deployed on an OSGi framework—the bundle runtime environment. It is a collaborative


environment, where the bundles run in the same VM and can actually share code. The framework uses
the explicit imports and exports to wire up the bundles, so they do not need to concern themselves with
class loading. A simple API allows bundles to install, start, stop, and update other bundles as well as
enumerate the bundles and their service usage.
Benefits of OSGi

• Reduced Complexity: There are no interdependencies between these bundles, so developers


have more freedom to change the bundles at a later stage. This simplifies application
development tasks process and reduces bugs .

ADOBE COPYRIGHT PROTECTED


• Reuse: The OSGi component model makes it very easy to use third-party components in an
application.

• Real World: The OSGi framework is dynamic and is a perfect match for many real-world
scenarios. Applications can reuse the powerful primitives of the service registry in their own
domain. This reduces the time for writing code and provides global visibility, debugging tools, and
more functionality.

• Easy Deployment: The OSGi technology specifies how components are installed and managed.
Therefore, it is easy to deploy and integrate OSGi technology in the existing and future systems.

• Dynamic Updates: The OSGi component model is a dynamic model. You can install, start, stop,
update, and uninstall bundles without bringing down the whole system.

• Adaptive: The dynamic service model of OSGi enables bundles to find out what capabilities are
available on the system and adapt the functionality they can provide. This makes code more
flexible and resilient to changes.

• Transparency: The management API provides access to the internal state of a bundle as well as
how it is connected to other bundles. For example, most frameworks provide a command shell
that shows this internal state. You can stop parts of the applications to debug a certain problem or
bring in diagnostic bundles. OSGi applications can often be debugged with a live command shell.

• Versioning: In the OSGi environment, all bundles are carefully versioned. Only the bundles that
can collaborate are wired together in the same class space. This enables various bundles to
function with their own library.

• Simple: Easy-to-use annotations inform the runtime how a class wants to use the dynamics,
configuration, and dependencies on other services. The default configurations completely hide the
dynamics and OSGi. This simple model enables the gradual use of more advanced features.

• Small: The OSGi Release 4 Framework can be implemented in about a 300KB JAR file. Therefore,
OSGi can be used on a large range of devices—from very small to small to mainframes.

Extend and Customize Adobe Experience Manager 141


• Runs Everywhere: The OSGi APIs do not use classes that are not available on all environments.
A bundle does not start if it contains the code that is not available in the execution environment.

Components

Components are the main building blocks for OSGi applications.


A component:
• Is provided by a bundle

• Is a piece of software managed by an OSGi container

• Is a Java object created and managed by a OSGi container.

ADOBE COPYRIGHT PROTECTED


• Can provide a service

• Can implement one or more Java interfaces as services

A component can publish itself as a service and/or can have dependencies on other components and
services. The OSGi container will activate a component only when all the required dependencies are
met or available. Each component has an implementation class, and can optionally implement a public
interface providing this service.
A service can be consumed or used by components and other services. Basically, a bundle needs the
following to become a component:
• An XML file, where you describe the service the bundle provides and the dependencies of other
services of the OSGi framework

• A manifest file header entry to declare that the bundle behaves as a component

• The activate and deactivate methods in the implementation class (or bind and unbind methods)

• Service Component Runtime (SCR). A service of the OSGi framework to manage these
components.

As a best practice, always upload the bundle using JCR. That way, the release engineers and system
administrators have one common mechanism for managing bundles and configurations.

Extend and Customize Adobe Experience Manager 142


Annotations in OSGi
With Declarative Services, OSGi components are developed using annotations to generate bundle
descriptors as well as metatype description for their configuration. Since AEM 6.2, the official OSGi
R6 Declarative Services Annotations are supported and Adobe recommends using those
annotations as they’re defined as a standard and allow for a simpler and cleaner code.
Projects based on the previously recommended Felix SCR annotations (now in maintenance
mode) can be easily migrated and both annotations styles can coexist within a bundle during the
migration phase.
The following annotations are supported:
• Component

ADOBE COPYRIGHT PROTECTED


• Activate

• Deactivate

• Modified

• Reference

@Component

The @Component annotation enables the OSGi Declarative Services to register your component. This is
the only required annotation for an OSGi component.
This annotation is used to declare the <component> element of the component declaration. The
required
<implementation> element is automatically generated with the fully qualified name of the class
containing the component annotation.

package com.adobe.osgitraining.impl;
import org.osgi.service.component.annotations.Component;

@Component

public class MyComponent {

Extend and Customize Adobe Experience Manager 143


Component Modifiers

With OSGi DS annotations, type and property definitions are not done using annotations, as they
are with Felix SCR annotations, but through component modifiers (or attributes).
In fact, what we’re doing when creating a component is to register the type of service (by
implementing one or several interfaces) and to declare the properties made available by metatype
generation, all this with the same annotation.
Some of the attributes you can use with the @Component annotation are:
• service: Types under which to register this component as a service

• properties: Property entries for this component

ADOBE COPYRIGHT PROTECTED


• name: Name of the component

• immediate: Indicates whether the component is immediately activated on bundle start


Example:

@Component (service=WorkflowProcess.class,
1.1.1.1.1.1 name=”My Custom Workflow",
1.1.1.1.1.2 property={“description=Workflow process to set approval status”,
“process.label=Approval Status Writer”})
1.1.1.1.1.3

When creating a Sling servlet, the servlet properties are defined in the property component
modifier like in this example:

@Component (service=Servlet.class,
name=”My Custom Sling Servlet",
property={“sling.servlet.extensions=html”,
“sling.servlet.selectors=foo”,
“sling.servlet.paths=/bin/foo”,
“sling.servlet.methods=get”,
“sling.servlet.resourceTypes=project/components/mycomponent”,
})

Extend and Customize Adobe Experience Manager 144


@Activate, @Deactivate, and @Modified

Methods annotated with these annotations specify what happens when the component is
respectively activated, deactivated or its configuration is modified.

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;

@Component
public class MyComponent{

ADOBE COPYRIGHT PROTECTED


@Activate
protected void activate() {
// do something
}

@Deactivate
protected void deactivate() {
// do something
}

@Modified
protected void readConfig() {
// get values
}
}

Extend and Customize Adobe Experience Manager 145


Configurable Services
The OSGi Configuration Admin Service enables components to get or retrieve configuration, and
provides the entry point for management agents to retrieve and update configuration data.
Configuration objects are identified by Persistent Identifiers (PID), and are bound to bundles when
used. For Declarative Services, the name of the component is used as the PID to retrieve the
configuration from the Configuration Admin Service.
Unlike SCR annotations, for which properties need to be read by overriding the activate() method,
OSGi DS annotations provide a mechanism for metatype declaration through an interface,
avoiding reading and converting each property. This interface is annotated with
@ObjectClassDefinition and defines each property with its type, using the annotations
@AttributeDefinition and @AttributeType.

ADOBE COPYRIGHT PROTECTED


Here’s an example:

import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;

@ObjectClassDefinition(name = My Configuration Service)

public @interface MyConfigInterface{

@AttributeDefinition(

name=“Enter a String”,
description=“Your description”,
type=AttributeType.STRING
)

String myconfig_property() default “”;

Extend and Customize Adobe Experience Manager 146


This interface is then defined in the component using the annotation @Designate. A modifier
named factory can be added to define the configuration as a factory.

import org.osgi.service.component.annontations.Component;
import org.osgi.service.metatype.annotations.Designate;

@Component

@Designate(ocd=MyConfigInterface.class, factory=true)

public class MyComponent {

private myString

@Activate

ADOBE COPYRIGHT PROTECTED


protected void activate(MyConfigInterface config) {

myString = config.myconfig_property()
}
}

Extend and Customize Adobe Experience Manager 147


Exercise 1: Implement a bundle activator
In this exercise, you will implement a bundle activator class that exposes a start method when the
bundle is started and run a stop method when the bundle is stopped:
This exercise includes two tasks:
1. Create a new java class
2. Deploy the project

Task 1: Create a new Java class

ADOBE COPYRIGHT PROTECTED


1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher
opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

NOTE: To activate the code inside the bundle, you need to implement the
org.osgi.framework.BundleActivtor interface.

Extend and Customize Adobe Experience Manager 148


4. Copy the file Activator.java from Exercise_Files/08_Deep_Dive_OSGi.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/08_Deep_Dive_OSGi/.


Copy and paste the file from the exercise files referenced to Activator.java to Eclipse. Please do not copy
the code from this exercise book. The code given here is only for illustrative purposes.

5. Right-click com.adobe.training.core and paste the file. The Activator.java class is


created, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: Activator.java uses the start() and stop() methods to create log entries based on the bundle
status.

1. package com.adobe.training.core;
2.
3. import org.osgi.service.component.annotations.Activate;
4. import org.osgi.service.component.annotations.Component;
5. import org.osgi.service.component.annotations.Deactivate;
6.
7. import org.slf4j.Logger;
8. import org.slf4j.LoggerFactory;
9.
10. @Component
11.
12. public class Activator {
13. private final Logger logger = LoggerFactory.getLogger(getClass());
14.
15. @Activate
16. public void startBundle() {
17. logger.info("##################Bundle Started##################");
18. }
19. @Deactivate
20. public void stopBundle() {
21. logger.info("##################Bundle Stopped##################");
22. } }

Extend and Customize Adobe Experience Manager 149


Task 2: Deploy the project

1. In Project Explorer, right-click training.core, and select Run As > Maven install. The build starts.

2. Verify that the bundle is installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. On your desktop, navigate to crx-quickstart\logs.

4. Open the log file project-trainingproject.log by using a text editor.

5. Scroll down to the bottom of the file, and observe the log messages that indicate that
the bundle is started, as shown:

Close the log file.

Extend and Customize Adobe Experience Manager 150


Exercise 2: Create and use a custom service
In this exercise, you will create and use a custom service.
This exercise includes three tasks:
1. Create a service and an implementation
2. Deploy the bundle and expose the service in HTL
3. Test the service
Task 1: Create a service and an implementation

1. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


2. Copy the file DeveloperInfo.java from Exercise_Files/08_Deep_Dive_OSGi.

3. Right-click com.adobe.training.core, and paste the file. The DeveloperInfo.java is


created, as shown:

Extend and Customize Adobe Experience Manager 151


4. Examine the service DeveloperInfo.java interface, as shown:
NOTE: The code is provided as part of the Exercise_Files under Exercise_Files/08_Deep_Dive_OSGi. Copy
and paste the file from the exercise files folder under com.adobe.training.core in Eclipse. Please do not copy
the code from this exercise book. The following code is only for illustrative purposes.
1. package com.adobe.training.core;
2. /**
3. * Service interface to get the information about the bundle Developer
4. *
5. * Example HTL:
6. * <h3 data-sly-
use.devInfo="com.adobe.training.core.DeveloperInfo">Developer Info: ${devInfo.DeveloperInfo}</h

ADOBE COPYRIGHT PROTECTED


3>
7. *
8. * Example code can be inserted into a HTL component:
9. * /apps/trainingproject/components/structure/page/partials/main.html
10. *
11. * Example JSP:
12. * com.adobe.training.core.DeveloperInfo devInfo = sling.getService(com.adobe.training.core.Develo
perInfo.class)
13. * <h3>Developer Info: <%= devInfo.getDeveloperInfo() %></h3>
14. *
15. * @author Kevin Nennig ([email protected])
16. */
17. public interface DeveloperInfo {
18. public String getDeveloperInfo();
19. }

5. Save the changes.

6. In Project Explorer, navigate to training.core > src/main/java, as shown:

Extend and Customize Adobe Experience Manager 152


7. Right-click src/main/java, and select New > Package. The New Java Package window
opens

8. Enter the following value for the Package field:


com.adobe.training.core.services.impl and click Finish.

9. Copy the file DeveloperInfoImpl.java from Exercises_Folder/08_Deep_Dive_OSGi.

NOTE: The code is provided as part of the Exercise_Files under Exercise_Files/08_Deep_Dive_OSGi.


Copy and paste the file from the exercise files referenced to DeveloperInfoImpl.java to Eclipse. Please
do not copy the code from this exercise book. The code given here is only for illustrative purposes.

ADOBE COPYRIGHT PROTECTED


10. Right-click com.adobe.training.core.services.impl, and paste the file. The
DeveloperInfoImpl.java class is created, as shown:

11. Examine the implementation of the Simple component of the DeveloperInfoImpl.java


class, as shown:

1. package com.adobe.training.core.services.impl;
2.
3. import org.osgi.service.component.ComponentContext;
4. import org.osgi.service.component.annotations.Activate;
5. import org.osgi.service.component.annotations.Component;
6. import org.osgi.service.component.annotations.Deactivate;
7. import org.osgi.service.component.annotations.Modified;
8. import org.slf4j.Logger;
9. import org.slf4j.LoggerFactory;
10.

Extend and Customize Adobe Experience Manager 153


11. import com.adobe.training.core.DeveloperInfo;
12.
13. /**
14. * Component implementation of the DeveloperInfo Service.
15. */
16.
17. @Component(service = DeveloperInfo.class, name = "TrainingDeveloperInfo")
18.
19. public class DeveloperInfoImpl implements DeveloperInfo {

ADOBE COPYRIGHT PROTECTED


20. private final Logger logger = LoggerFactory.getLogger(getClass());
21.
22. //local variables to hold OSGi config values
23. private boolean showDeveloper;
24. private String developerName;
25. private String[] developerHobbiesList;
26. private String langPreference;
27.
28. @Activate
29. @Modified
30. //http://blogs.adobe.com/experiencedelivers/experience-
management/osgi_activate_deactivatesignatures/
31. protected void activate(ComponentContext config) {
32. logger.info("#############Component config saved");
33. }
34.
35. @Deactivate
36. protected void deactivate() {
37. logger.info("#############Component (Deactivated) Good-bye " + developerName);
38. }
39.
40. /* Method used to show a simple OSGi service/component relationship */
41. public String getDeveloperInfo(){

Extend and Customize Adobe Experience Manager 154


42. return "Hello! I do not know who my developer is. I am a product of random development
!!!"; } }

12. Examine the method getDeveloperInfo() in the DeveloperInfoImpl.java class.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 155


Task 2: Deploy the bundle and expose the service in HTL

1. In Project Explorer, right-click training.core and select Run As > Maven install. The
build starts.

2. Verify that the bundle is installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. Open a browser, and go to the AEM Web Console,
http://localhost:4502/system/console. The Adobe Experience Manager Web Console
Bundles page opens.

4. Expand the TrainingProject – Core (com.adobe.trianing.core), as shown:

5. Scroll down, and look for the Service, com.adobe.training.core.DeveloperInfo.

Extend and Customize Adobe Experience Manager 156


6. Verify that the service exists in the bundles, as shown:

This indicates that the class was successfully installed in the OSGI.

7. In Eclipse, under Project Explorer, navigate training.ui.apps >


src/main/content/jcr_root [nt:folder] > apps [nt:folder] > trainingproject [nt:folder]
> components [nt:folder] > content [nt:folder] > helloworld [cq:Component].

ADOBE COPYRIGHT PROTECTED


8. Expand helloworld [cq:Component] and double-click helloworld.html. The file opens
in XML Editor.

9. Add the following line to the helloworld.html page:

 <h3 data-sly-use.devInfo="com.adobe.training.core.DeveloperInfo">Developer Info:


${devInfo.DeveloperInfo} </h3>

 NOTE: This code is also available in the file under Exercise_Files/08_Deep_Dive_OSGi/html.txt.

10. Save changes to the file.

NOTE: With HTL, you can consume an OSGi service. Here, you are consuming the DeveloperInfo
service. After the service is is called, you can use the method from the service.
NOTE: The Eclipse AEM server with automatically sync the HTL file after it is saved. Make sure you
have your Eclipse AEM server started otherwise the file will not sync with the JCR.

Extend and Customize Adobe Experience Manager 157


11. To verify whether your file successfully saved to the JCR, open CRXDE Lite to
/apps/trainingproject/components/content/helloworld/ helloworld.html and see if
that file contains your change.

12. If your changes are missing, you can force an update with full redeploy to the JCR by
right-clicking training.ui.apps and selecting Run AS > Maven Install

13. Verify the bundle is installed successfully.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 158


Task 3: Test the service

1. Log in to AEM, and click Adobe Experience Manager in the upper-left corner.

2. Click Navigation > Sites. The Sites console opens.

3. Select TrainingProject Site > English, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 159


4. Click Edit (e), as shown:

ADOBE COPYRIGHT PROTECTED


5. Verify you see the message, as shown:

Extend and Customize Adobe Experience Manager 160


Exercise 3: Code OSGi configurations
In this exercise, you will code the custom OSGi configurations for an OSGi component. This will allow an
administrator to configure the component as we did earlier. These configurations can then be further
targeted by using run modes.
This exercise includes three tasks:
1. Update the OSGi component
2. Deploy the bundle
3. Test the service

Task 1: Update the OSGi component

In Eclipse, navigate to training.core > src/main/java >

ADOBE COPYRIGHT PROTECTED


1.
com.adobe.training.core.services.impl, as shown:

2. Double-click DeveloperInfoImpl.java to open it in XML Editor.

3. Copy the contents from Exercise_Files/08_Deep_Dive_OSGi/DeveloperInfoImpl-


v2.java to DeveloperInfoimpl.java in Eclispe, as shown:

1. package com.adobe.training.core.services.impl;
2.

3. import org.osgi.service.component.annotations.Activate;
4. import org.osgi.service.component.annotations.Component;
5. import org.osgi.service.component.annotations.Deactivate;
6. import org.osgi.service.component.annotations.Modified;
7. import org.osgi.service.metatype.annotations.Designate;
8.
9. import org.slf4j.Logger;
10. import org.slf4j.LoggerFactory;
11.

Extend and Customize Adobe Experience Manager 161


12. import java.util.Arrays;
13.
14. import com.adobe.training.core.DeveloperInfo;
15. import com.adobe.training.core.services.DeveloperInfoConfiguration;
16.
17. /**
18. * Component implementation of the DeveloperInfo Service. This gets the developer info from t
he OSGi Configuration
19. * There are 4 OSGi Configuration Examples:

ADOBE COPYRIGHT PROTECTED


20. * -Boolean
21. * -String
22. * -String Array
23. * -Dropdown
24. */
25.
26. @Component(service = DeveloperInfo.class, name = "TrainingDeveloperInfo")
27.
28. @Designate(ocd = DeveloperInfoConfiguration.class)
29. public class DeveloperInfoImpl implements DeveloperInfo {
30. private final Logger logger = LoggerFactory.getLogger(getClass());
31.
32. //local variables to hold OSGi config values
33. private boolean showDeveloper;
34. private String developerName;
35. private String[] developerHobbiesList;
36. private String langPreference;
37.
38. @Activate
39. @Modified
40. //http://blogs.adobe.com/experiencedelivers/experience-
management/osgi_activate_deactivatesignatures/
41. protected void activate(DeveloperInfoConfiguration config) {
42.

Extend and Customize Adobe Experience Manager 162


43. showDeveloper = config.developerinfo_showinfo();
44. developerName = config.developerinfo_name();
45. developerHobbiesList = config.developerinfo_hobbies();
46. langPreference = config.developerinfo_language();
47. logger.info("#############Component config saved");
48. }
49.
50. @Deactivate
51. protected void deactivate() {

ADOBE COPYRIGHT PROTECTED


52. logger.info("#############Component (Deactivated) Good-bye " + developerName);
53. }
54.
55. /**
56. * Method used to show how OSGi configurations can be brought into a OSGi component
57. **/
58. public String getDeveloperInfo(){
59.
60. String developerHobbies = Arrays.toString(developerHobbiesList);
61.
62. if(showDeveloper)
63.
64. return "Created by " + developerName
65. + ". <br>Hobbies include: " + developerHobbies
66. + ". <br>Preferred programming language in AEM is " + langPreference;
67. return "";
68. }
69. /*
70. * Method used to show a simple OSGi service/component relationship
71. public String getDeveloperInfo(){
72. return "Hello! I do not know who my developer is. I am a product of random development!
!!";
73. } */ }
NOTE: Ignore errors that mention Developer Info Configuration. We will fix the errors in a few steps.

Extend and Customize Adobe Experience Manager 163


4. Examine the modified code. Notice the name of the component at the beginning of
the class TrainingDeveloperInfo, as shown:

5. Examine the various methods used in the class such as activate(), deactivate() and
getDeveloperInfo() to understand the logic behind them.

Notice the activate method takes a special class called DeveloperInfoConfiguration. This is a custom

ADOBE COPYRIGHT PROTECTED


configuration class where you set what properties should be exposed to OSGi through the Web
Console.

6. In Eclipse Project Explorer, right click on com.adobe.training.core and choose New >
Class.

7. Enter the following value for the package and name:

• Package: com.adobe.training.core.services

• Name: DeveloperInfoConfiguration

8. Click Finish.

9. Copy the contents of the file DeveloperInfoConfiguration .java from /Exercises_Folder/08_Deep


Dive into OSGi to DeveloperInfoConfiguration in Eclipse.

NOTE: The code is provided as part of the Exercise_Files under


/Exercise_Files/08_Deep_Dive_OSGi/. Copy and paste the file from the exercise files referenced to
DeveloperInfoConfiguration.java to Eclipse. Do not copy the code from this exercise book. The code
given here is only for illustrative purposes.

Extend and Customize Adobe Experience Manager 164


10. Examine DeveloperInfoConfiguration.java, as shown:

1. package com.adobe.training.core.services;
2.
3. import org.osgi.service.metatype.annotations.AttributeDefinition;
4. import org.osgi.service.metatype.annotations.ObjectClassDefinition;
5. import org.osgi.service.metatype.annotations.AttributeType;
6. import org.osgi.service.metatype.annotations.Option;
7.
8. @ObjectClassDefinition(name = "Developer Info Service")

ADOBE COPYRIGHT PROTECTED


9. public @interface DeveloperInfoConfiguration {
10.
11. @AttributeDefinition(
12. name = "Show Info",
13. description = "Should the Developer information be shown?",
14. type = AttributeType.BOOLEAN
15. )
16. boolean developerinfo_showinfo() default false;
17.
18. @AttributeDefinition(
19. name = "Name",
20. description = "Name of the Developer",
21. type = AttributeType.STRING
22. )
23. String developerinfo_name() default "";
24.
25. @AttributeDefinition(
26. name = "Hobbies",
27. description = "List your favorite Hobbies",
28. type = AttributeType.STRING
29. )
30. String[] developerinfo_hobbies() default {"swimming", "climbing"};
31.

Extend and Customize Adobe Experience Manager 165


32. @AttributeDefinition(
33. name = "Language",
34. description = "Favorite Language Preference",
35. options = {
36. @Option(label = "HTL", value = "HTL"),
37. @Option(label = "Java", value = "Java"),
38. @Option(label = "JSP", value = "JSP"),
39. @Option(label = "HTML", value = "HTML"),
40. @Option(label = "JavaScript", value = "JavaScript")

ADOBE COPYRIGHT PROTECTED


41. }
42. )
43. String developerinfo_language() default "";
44. }

11. Save the changes.

NOTE: You get the configurations in the activate() method by using the methods defined
in the DeveloperInfoConfiguration class.

Extend and Customize Adobe Experience Manager 166


Task 2: Deploy the bundle

1. In Project Explorer, right-click training.core, and select Run As > Maven install. The build starts.

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 167


Task 3: Test the service

1. Navigate to the Web Console in AEM (http://localhost:4502/system/console/configMgr). The


Adobe Experience Manager Web Console page opens.

2. Under OSGi > Configuration, search for Developer Info.

3. Click Develop Info Service. The Developer Info Service opens, as shown:

ADOBE COPYRIGHT PROTECTED


4. Carry out the following steps:

a. Select the Show Info checkbox.


b. Enter Scott Reynolds in the Name field.

5. Save the changes.

6. Navigate to Sites. The Sites console opens.

7. Select TrainingProject Site > English > Edit (e) to open the page in a new tab in your browser.

Extend and Customize Adobe Experience Manager 168


8. Verify the following message, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: In this task, you updated the OSGi configurations from the Web Console. In a realistic
situation, you should create an OSGi config node in the JCR.

Extend and Customize Adobe Experience Manager 169


References
For more information on OSGi, refer:

https://www.osgi.org/developer/architecture/

http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html

OSGi Components – Simply Simple – Part I

https://blog.osoco.de/2015/08/osgi-components-simply-simple-part-i/

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 170


Module 9: Working with the Sling Web Framework

Introduction

Apache sling is an open source web application framework that makes it easy to develop content-oriented
applications. Apache sling:

• Is a Representational State Transfer (REST)-based web framework

ADOBE COPYRIGHT PROTECTED


• Is content-driven and using a JCR content repository

• Is powered by OSGi

• Uses scripts or Java Servlets to process HTTP requests

• Is resource-oriented and maps into JCR nodes

Apache sling applications are a set of OSGi bundles that use the OSGi core and compendium services. The
Apache Felix OSGi framework and console provide a dynamic runtime environment, in which the code
and content bundles can be loaded, unloaded, and reconfigured at runtime.

In Apache Sling, a request URL is first resolved to a resource. Based on this resource, as well as the request
method and more properties of the request URL, a script or servlet is then selected to handle the request.

Objectives

• Describe Apache Sling

• Describe sling resolution process

• Create Sling servlets

• Configure Sling POST servlet

• Create system users

Extend and Customize Adobe Experience Manager 171


Understanding Sling Resolution Process
Apache Sling is resource oriented, and maintains all the resources in the form of a virtual
tree. A resource is usually mapped to a JCR node, but can also be mapped to a file system or
database.
following are some of the common properties that a resource can have are:
• Path – Includes the names of all resources beginning from the root, each separated by a slash (/).
It is similar to a URL path or file system path.

• Name – This is the last name in the resource path.

• Resource Type - Each resource has a resource type that is used by the Servlet and Script resolver
to find the appropriate Servlet or Script to handle the request for the Resource.

ADOBE COPYRIGHT PROTECTED


When using Sling, the type of content to be rendered is not the first processing consideration.
Instead, the main consideration is whether the URL resolves to a content object, for which a
script can then be found to perform the rendering. The advantages of this flexibility are
apparent in applications.
Resource First Request Processing

When a request URL comes in, it is first resolved to a resource. Then, based on the resource, it selects the
servlet or script to handle the request.

Extend and Customize Adobe Experience Manager 172


Basic Steps of Processing Requests

Each content item in the JCR repository is exposed as an HTTP resource, so the request URL
addresses the data to be processed, not the procedure that does the processing. After the
content is determined, the script or servlet to be used to handle the request is determined in
cascading resolution that checks in the order of the priority as follows:
1. Properties of the content item itself
2. The HTTP method used to make the request
3. A simple naming convention within the URL that provides secondary information
For every URL request, the following steps are performed to get it resolved:
1. Decompose the URL

ADOBE COPYRIGHT PROTECTED


4. Mapping Request to resources
5. Resolve the Resource
6. Resolve the rendering script/servlet
Decomposing the URL

Processing is done based on the URL requests submitted by the user. The elements of the
URL are extracted from the request to locate and access the appropriate script.
Example - URL: http://myhost/tools/spy.printable.a4.html/a/b?x=12
The above URL can be decomposed into the following components:

The following table describes the components.


Protocol Hypertext transfer protocol
Host Name of the website
Content path Path specifying the content to be rendered. It is used in combination
with the extension.
Selector(s) Used for alternative methods of rendering the content
Extension Content format. Also specifies the script to be used for rendering.
Suffix Can be used to specify additional information
Param(s) Any parameters required for dynamic content

Extend and Customize Adobe Experience Manager 173


Mapping Request to Resources

After the URL is decomposed, the content node is located from the content path. This node is
identified as the resource, and performs the following steps to map to the request.
Consider the URL request: http://myhost/tools/spy.html
• Sling checks whether a node exists at the location specified in the request.
In the above example, it searches for the node spy.html.

• If no node is found at that location, the extension is dropped and the search is repeated. For
example, it searches for the node spy.

• If no node is found, then Sling returns the http code 404 (Not Found).

ADOBE COPYRIGHT PROTECTED


• If a node is found, then the sling resource type for that node is extracted, and used to locate the
script to be used for rendering the content.

Resolve the Rendering Scripts

All scripts are stored in either the /apps or /libs folder, and are searched in the same order. If
no matching script is found in either of the folders, then the default script is rendered. When
the resource is identified from the URL, its resource type property is located and the value is
extracted. This value is either an absolute or a relative path that points to the location of the
script to be used for rendering the content.
For multiple matches of the script, the script with the best match is selected. The more the
selector matches, the better.
Example of rendering the content:

Super types are also taken into consideration when trying to locate a script. The advantage of
resource super types is that they may form a hierarchy of resources where the default
resource type sling/servlet/default (used by the default servlets) is effectively the root.
The resource super type of a resource may be defined in two ways:
Extend and Customize Adobe Experience Manager 174
1. sling:resourceSuperType property of the resource.

2. sling:resourceSuperType property of the node to which the sling:resourceType points.

To summarize how a script is resolved:

ADOBE COPYRIGHT PROTECTED


The Resource Resolver

The Resource Resolver is a part of Sling that resolves incoming requests to actual or virtual
resources. For example, a request for /training/english will be resolved to a corresponding
JCR node. However, if you do not want to expose the internal JCR structure, or if it cannot be
resolved to a JCR node, you can define a set of resolver rules through the Web Console. You
can also use the standard OSGi configuration mechanisms in the CRXDE Lite to define a set
of resolver rules.
The Resource Resolver abstracts:
• The path resolution

• Access to the persistence layer(s)

Resource mapping is used to define redirects, vanity URLs, and virtual hosts. You can access
the Resolver Map entries through the Resource Resolver tab of the Web Console. You can
access the console with this link: http://localhost:4502/system/console/jcrresolver

Extend and Customize Adobe Experience Manager 175


Mappings for Resource Resolution

The following node types help with the definition of redirects and aliases.

Node Types Description

sling:ResourceAlias Mixin node type defines the sling:alias property, and may be attached to any
node, which does not otherwise allow the setting of a property named
sling:alias.

sling:MappingSpec Mixin node type defines the sling:match, sling:redirect,


sling:status, and sling:internalRedirect properties

sling:Mapping The primary node type used to construct entries in /etc/map

ADOBE COPYRIGHT PROTECTED


The following properties are important when dealing with new resources.

Properties Description

sling:match Defines a partial regular expression used on a node’s name to match the
incoming request.

sling:redirect Causes a redirect response to be sent to the client

sling:status Defines the HTTP status code sent to the client

sling:internalredirect Causes the current path to be modified internally to continue with resource
resolution

sling:alias Indicates an alias name for the resource

Each entry in the mapping table is a regular expression, which is constructed from the
resource path below /etc/map. The following rules apply:
• If any resource along the path has a sling:match property, the respective value is used in the
corresponding segment instead of the resource name.

• Only resources having a sling:redirect or sling:internalRedirect property are used as table entries.
Other resources in the tree are just used to build the mapping structure.

Extend and Customize Adobe Experience Manager 176


Example of Resource Mapping

Consider the following content:


/etc/map
+-- http
+-- example.com.80
| +-- sling:redirect = "http://www.example.com/"
+-- www.example.com.80
| +-- sling:internalRedirect = "/example"
+-- any_example.com.80
| +-- sling:match = ".+\.example\.com\.80"
| +-- sling:redirect = "http://www.example.com/"

ADOBE COPYRIGHT PROTECTED


+-- localhost_any
| +-- sling:match = "localhost\.\d*"
| +-- sling:internalRedirect = "/content"
| +-- cgi-bin
| | +-- sling:internalRedirect = "/scripts"
| +-- gateway
| | +-- sling:internalRedirect = "http://gbiv.com"
| +-- (stories)
| +-- sling:internalRedirect = "/anecdotes/$1"
+-- regexmap
+-- sling:match = "$1.example.com/$2"
+-- sling:internalRedirect = "/content/([^/]+)/(.*)"

Based on the above definition, the following are the mappings:

Regular Expression Redirect Internal Description

http/example.com.80 http://www.example.com no Redirects all requests to the Second Level


Domain to www

http/www.example.com.80 /example yes Prefixes the URI paths of the requests sent to
this domain with the string /example

http/.+.example.com.80 http://www.example.com no Redirects all requests to sub domains to www.


The actual regular expression for the host.port
segment is taken from the sling:match property.

http/localhost.\d* /content yes Prefixes the URI paths with /content for
requests to localhost, regardless of actual port
the request was received on. This entry only
applies if the URI path does not start with

Extend and Customize Adobe Experience Manager 177


/cgi-bin, gateway or stories because there are
longer match entries. The actual regular
expression for the host.port segment is taken
from the sling:match property.

http/localhost.\d*/cgi-bin /scripts yes Replaces the /cgi-bin prefix in the URI path
with /scripts for requests to localhost, regardless
of actual port the request was received on.

http/localhost.\d*/gateway http://gbiv.com yes Replaces the /gateway prefix in the URI path
with http://gbiv.com for requests to localhost,
regardless of actual port the request was
received on.

ADOBE COPYRIGHT PROTECTED


http/localhost.\d*/(stories) /anecdotes/stories yes Prepends the URI paths starting
with /stories with /anecdotes for requests to
localhost, regardless of actual port the request
was received on.

Extend and Customize Adobe Experience Manager 178


Working with Sling Servlets
Servlets can be registered as OSGi services. Apache sling applications use scripts or Java
servlets, selected based on simple name conventions, to process HTTP requests in a RESTful
way. Being a REST framework, Apache Sling is oriented around resources, which usually map
to JCR nodes. With Apache sling, a request URL is first resolved to a resource, and then based
on the resource, it selects the actual servlet or script to handle the request.
The GET method has default behaviors and, therefore, requires no additional parameters. By
decomposing the URL, Apache sling can determine the resource URL and other information
that can be used to process that resource.
The POST method is handled by the Sling PostServlet. The PostServlet enables writes to a
data store (usually JCR) directly from HTML forms or from the command line by using cURL.

ADOBE COPYRIGHT PROTECTED


You can view the existing servlets through the Web Console or you can click:
http://localhost:4502/system/console/configMgr

Configuring the Default Sling GET Servlet


Apache Sling provides the default renderers for the following mime types of the default GET
servlet:
• txt

• JSON

• docview.xml

• sysview.xml

• html

You can use the Configuration tab of the Web Console to configure the GET servlet, as
shown:

Extend and Customize Adobe Experience Manager 179


Configuring the Sling POST Servlet
The Sling POST Servlet provides a RESTful interface that enables you to customize the data content
stored in the AEM JCR. This servlet maps nodes in the JCR repository to URIs and enables applications to
modify the JCR content. A Sling POST Servlet is performed on the client by using JavaScript.
To create content in the JCR, send an http POST request with the path of the node where you want to
store the content and the actual content, sent as request parameters. For example, consider the
following script:

1. <form method="POST" action="http://host/mycontent"enctype="multipart-form/data">


2. <input type="text" name="title" value=""/>
3. <input type="text" name="title" value="" />

ADOBE COPYRIGHT PROTECTED


4. </form>

NOTE: This code will work only if the com.adobe.granite.csrf.impl.CSRFFilter filter is defined in OSGi
configurations.
The above code will set the title and text properties on a node in the location /mycontent. If
this node does not exist, it will be created. Otherwise, the existing content at that URL would
be modified.
You can perform a similar operation using the following cURL commands:

1. curl -u admin:admin -F"jcr:primaryType=nt:unstructured" -Ftitle="some title text" -


Ftext="some body text content" http://host/some/new/content
2. curl -u admin:admin -F":operation=delete" http://host/content/sample

Extend and Customize Adobe Experience Manager 180


You can use the Configuration tab of the Web Console to configure the Sling POST servlet,
as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 181


Exercise 1: Create a Sling Servlet
In this exercise, you will write a servlet that serves only DOGET requests from a specific URI or
resource type. The response shall contain the JCR repository properties as JSON.
This exercise includes two tasks:
1. Create a servlet with a path
2. Create a servlet with a resource type

Task 1: Create a servlet with a path

1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher

ADOBE COPYRIGHT PROTECTED


opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

Extend and Customize Adobe Experience Manager 182


4. Copy the file TitleSlingServlet.java from Exercise_Files/09_Sling_Web_Framework.

NOTE: The code is provided as part of the Exercise_Files under


/Exercise_Files/09_Sling_Web_Framework/. Copy and paste the file from the exercise files
referenced to TitleSlingServlet.java to Eclipse. Please do not copy the code from this
exercise book. The code in the student guide is only for illustrative purposes.

5. Right-click com.adobe.training.core.servlets and paste the file. The


TitleSlingServlet.java class is created, as shown:

ADOBE COPYRIGHT PROTECTED


6. Observe that the servlet Path mentioned in the code is
/bin/trainingproject/titleservlet. So the request can be served on this path, as shown:

1. package com.adobe.training.core.servlets;
2.
3. import java.io.IOException;
4.
5. import javax.servlet.Servlet;
6. import javax.servlet.ServletException;
7. import org.apache.sling.api.SlingHttpServletRequest;
8. import org.apache.sling.api.SlingHttpServletResponse;
9. import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
10.
11. import org.osgi.service.component.annotations.Component;
12.
13. @Component( service = Servlet.class,
14. name="TrainingTitleServlet",
15. property = {
16. "sling.servlet.paths=/bin/trainingproject/titleservlet",
17. "sling.servlet.extensions=html"
18. })
19.
20. //TitleSlingServlet uses resourceTypes and extensions to bind to URLs
21.

Extend and Customize Adobe Experience Manager 183


22. /*@Component( service = Servlet.class,
23. name="TrainingTitleServlet",
24. property = {
25. "sling.servlet.resourceTypes=/apps/trainingproject/components/content/title",
26. "sling.servlet.extensions=html"
27. })*/
28.
29.
30. public class TitleSlingServlet extends SlingSafeMethodsServlet {
31.
32. private static final long serialVersionUID = 1L;
33.
34. @Override

ADOBE COPYRIGHT PROTECTED


35. protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
36. response.setHeader("Content-Type", "text/html");
37. response.getWriter().print("<h1>Sling Servlet injected this title</h1>");
38. response.getWriter().close();
39. }
40. }

7. In Project Explorer, right-click training.core and select Run As > Maven install. The
build starts.

8. Verify the bundle installed successfully, as shown:

Extend and Customize Adobe Experience Manager 184


9. Verify you can call the title servlet through
http://localhost:4502/bin/trainingproject/titleservlet, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 185


Task 2: Create a servlet with a resource type

1. In Project Explorer, edit TitleSlingServlet to change the servlet implementation by


commenting the following, as shown:

1. /* @Component( service = Servlet.class,


2. name="TrainingTitleServlet",
3. property = {
4. "sling.servlet.paths=/bin/trainingproject/titleservlet",
5. "sling.servlet.extensions=html"
6. })*/

2. Uncomment the following lines, as shown:

ADOBE COPYRIGHT PROTECTED


1. @Component( service = Servlet.class,
2. name="TrainingTitleServlet",
3. property = {
4. "sling.servlet.resourceTypes=/apps/trainingproject/components/content/title",
5. "sling.servlet.extensions=html"
6. })

3. Right-click training.core, and select Run As > Maven install to build the project. The
project is built.

4. Open http://localhost:4502/content/trainingproject/en.html and see the output, as


shown:

Extend and Customize Adobe Experience Manager 186


Notice how the servlet finds all the resources on the page by using
apps/trainingproject/components/content/title and injects the response from the servlet.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 187


Exercise 2: Add content with Sling post servlet
There are multiple ways to modify content. SlingPostServlet is one of the ways to create, modify, copy,
move, delete and import the content.
In this exercise, you will migrate the content using the cURL commands. The commands will help
achieve the following:
1. Create a Page
2. Lock a Page
3. Add content to a page
4. Unlock the page
5. Publish the page

ADOBE COPYRIGHT PROTECTED


1. On the OS command line, execute the following command to create a page, as shown:
curl -u admin:admin -X POST -d "jcr:primaryType=cq:Page" http://localhost:4502/content/we-retail/language-masters/en/sling-
page

NOTE: The commands are available as part of


/Exercises_Folder/09_Sling_Web_Framework/sling-curl-commands.txt

2. Verify that the command to create a page ran successfully by checking for the status
Created, as shown:

Extend and Customize Adobe Experience Manager 188


3. Run the next command to add content to the sling page, as shown:

curl -u admin:admin -X POST -


d "jcr:primaryType=cq:PageContent&sling:resourceType=weretail/components/structure/page&cq:template=/conf/we-
retail/settings/wcm/templates/content-page&jcr:title=Sling cURL Page" http://localhost:4502/content/we-
retail/language-masters/en/sling-page/jcr:content

ADOBE COPYRIGHT PROTECTED


4. Navigate to Sites > We.Retail > Language Masters > English, and notice the content is
added to the Sling Page, as shown:

5. Run the command to lock the page, as shown:

curl -u admin:admin -X POST -F cmd="lockPage" -F path="/content/we-retail/language-masters/en/sling-page" -F "_charset_"="utf-


8" http://localhost:4502/bin/wcmcommand

Extend and Customize Adobe Experience Manager 189


6. Verify that the command to lock the page ran successfully by checking for the status
Page locked, as shown:

ADOBE COPYRIGHT PROTECTED


7. Navigate to Sites > We.Retail > Language Masters > English, and click the thumbnail
on the Sling cURL page.

8. Click Edit (e). The Sling cURL page opens in a new tab.

9. Notice the page is locked, as shown:

Extend and Customize Adobe Experience Manager 190


10. Run the command to add content to the page, as shown:

curl -u admin:admin -X POST -


d "sling:resourceType=core/wcm/components/text/v2/text&text=<h3>I am a new Text Componen
t from the Sling</h3>&textIsRich=true" http://localhost:4502/content/we-retail/language-
masters/en/sling-page/jcr:content/root/responsivegrid/*

11. Verify that the command to add content to the page ran successfully by checking for
the status i_am_a_new_text, as shown:

ADOBE COPYRIGHT PROTECTED


12. Go to http://localhost:4502/editor.html/content/we-retail/language-masters/en/sling-
page.html, and verify the content is added to the page, as shown:

Extend and Customize Adobe Experience Manager 191


13. Run the command to unlock the page, as shown:

curl -u admin:admin -X POST -F cmd="unlockPage" -F path="/content/we-retail/language-


masters/en/sling-page" -F "_charset_"="utf-8" http://localhost:4502/bin/wcmcommand

14. Verify that the command to unlock the page ran successfully by checking for the status,
Page unlocked, as shown:

ADOBE COPYRIGHT PROTECTED


15. Go to http://localhost:4502/editor.html/content/we-retail/language-masters/en/sling-
page.html, and verify the page is unlocked for editing, as shown:

Extend and Customize Adobe Experience Manager 192


16. Run the command to activate the page, as shown:

curl -u admin:admin -X POST -F path="/content/we-retail/language-masters/en/sling-


page" -F cmd="activate" http://localhost:4502/bin/replicate.json

17. Verify that the command to activate the page ran successfully by checking for the status,
Replication Started, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 193


18. Go to http://localhost:4502/editor.html/content/we-retail/language-masters/en/sling-
page.html, and verify the page is activated.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 194


Creating System Users
Adobe suggests developers create a system user that map to a subsystem using a service user mapper.
A service user is a JCR user with no password set and a minimal set of privileges that are necessary to
perform a specific task. Having no password set means that it will not be possible to log in with a service
user.

Problem
To access the data storage in the Resource Tree and/or JCR, authentication is required to properly set up
access control and guard sensitive data from unauthorized access. For regular request processing, this
authentication step is handled by the Sling Authentication subsystem.

ADOBE COPYRIGHT PROTECTED


On the other hand, there are also some background tasks to be executed with access to the resources.
Such tasks cannot in general be configured with user names and passwords. Neither hard coding the
passwords in the code nor having the passwords in – more or less – plain text in some configuration is
considered a good practice.
The solution presented here serves the following goals:
• Prevent overuse and abuse of administrative ResourceResolvers and/or JCR Sessions.

• Allow services access to ResourceResolvers and/or JCR Sessions without requiring to hard-code or
configure passwords.

• Allow services to use service users that are specially configured for service level access (usually
done on unix systems).

• Allow administrators to configure the assignment of service users to services.

Concept of Service

A service is a piece or collection of functionality. The examples of services are the Sling queuing system,
Tenant Administration, or a Message Transfer System. Each service is identified by a unique service
name. Because a service is implemented in an OSGi bundle or a collection of OSGi bundles, services are
named by the bundles providing them.
A service may be comprised of multiple parts, so each part of the service may be further identified by a
subservice name. This subservice name is optional though. The examples of subservice name are the
names for subsystems in a Message Transfer System, such as accepting messages, queueing messages,
and delivering messages.
The combination of the Service Name and Subservice Name defines the Service ID. The Service ID is finally
mapped to a Resource Resolver and/or JCR user ID for authentication.
Therefore, the actual service identification (service ID) is defined as:
service-id = service-name [ ":" subservice-name ].
The service-name is the symbolic name of the bundle providing the service.

Extend and Customize Adobe Experience Manager 195


Implementation of Service Authentication:

The implementation in Apache sling of the service authentication concept described above consists of
three parts: ServiceUserMapper, ResourceResolverFactory, and SlingRepository.

ServiceUserMapper
The ServiceUserMapper service allows for the mapping Service IDs comprised of the Service Names
defined by the providing bundles and optional Subservice Name to ResourceResolver and/or JCR
Repository user IDs. This mapping is configurable such that system administrators are in full control of
assigning users to services.
The ServiceUserMapper defines the following API:

ADOBE COPYRIGHT PROTECTED


String getServiceUserID(Bundle bundle, String subServiceName);

ResourceResolverFactory

The second part is support for service access to the Resource Tree. The ResourceResolverFactory service
is enhanced with a new factory method, as shown below:

ResourceResolver getServiceResourceResolver(Map<String, Object> authenticationInfo) throws


LoginException;

This method allows for access to the resource tree for services where the service bundle is the bundle
actually using the ResourceResolverFactory service. The optional Subservice Name may be provided as
an entry in the authenticationInfo map.

In addition to having new API on the ResourceResolverFactory service to be used by services, the
ResourceProviderFactory service is updated with support for Service Authentication.

SlingRepository

The third part is an extension to the SlingRepositoryservice interface to support JCR Repository access for
services:

Session loginService(String subServiceName, String workspace) throws LoginException,


RepositoryException;

This method allows for access to the JCR Repository for services, where the service bundle is the bundle
actually using the SlingRepository service. The additional Subservice Name may be provided with the
subServiceName parameter.

Extend and Customize Adobe Experience Manager 196


Deprecation of administrative authentication

Originally the ResourceResolverFactory.getAdministrativeResourceResolver and


SlingRepository.loginAdministrative methods were defined to provide access to the resource tree and JCR
Repository. These methods proved to be inappropriate because they allowed for much broad access.

Consequently, these methods are being deprecated and will be removed in future releases of the service
implementations.

The following methods are deprecated:

• ResourceResolverFactory.getAdministrativeResourceResolver

ADOBE COPYRIGHT PROTECTED


• ResourceProviderFactory.getAdministrativeResourceProvider

• SlingRepository.loginAdministrative

The implementations in Sling's bundle will remain implemented in the near future. However, there will be a
configuration switch to disable support for these methods: If the method is disabled, a LoginException is
always thrown from these methods. The JavaDoc of the methods is extended with this information.

Extend and Customize Adobe Experience Manager 197


Exercise 3: Create a system user
In this exercise, you will create a system user, provide access rights to the user.
This exercise includes two tasks:

1. Create a service user

2. Create the mapping to the user

Task 1: Create a service user

1. Navigate to: http://localhost:4502/crx/explorer.

ADOBE COPYRIGHT PROTECTED


2. Log in as admin/admin. The Content Repository console opens, as shown:

Extend and Customize Adobe Experience Manager 198


3. In the window, click Create System User, as shown. The Create New System User wizard opens.

ADOBE COPYRIGHT PROTECTED


4. In the UserID field, enter training-user, as shown:

5. Click the green checkmark in the lower-right corner of the dialog box.

Extend and Customize Adobe Experience Manager 199


6. Click Close. The training-user is created.

ADOBE COPYRIGHT PROTECTED


7. Go to http://localhost:4502/useradmin. The AEM Security console opens, as shown:

Extend and Customize Adobe Experience Manager 200


8. Find the administrators group and double-click it to open, as shown:

ADOBE COPYRIGHT PROTECTED


9. Click the Members tab. The Members tab opens.

10. On the left, search for training-user by using the search option, as shown:

Extend and Customize Adobe Experience Manager 201


11. Drag the training-user into the Members tab. The training-user is added to the Members list, as shown:

ADOBE COPYRIGHT PROTECTED


12. Click Save, as shown:

Extend and Customize Adobe Experience Manager 202


Task 2: Deploy the bundle

In this task, you will create the mapping from our bundle to the user with an OSGi configuration
node. The configuration we are going to setup is the ServiceUserMapperImpl.

1. Navigate to CRXDE Lite: http://localhost:4502/crx/de

2. Under /apps/trainingproject/config, right-click config and choose Create >Create node.

• Name: org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-
training

ADOBE COPYRIGHT PROTECTED


• Type: sling:OsgiConfig

3. Click OK and save the changes. The node


org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-training is created.

4. Add the following two properties to the node:

• Name=service.ranking, type=Long, value=0

• Name=user.mapping, type=String,
value=com.adobe.training.core:training=training-user

The user.mapping allows us to map a bundle with a subservice name to a service user, as shown:

Bundle: com.adobe.training.core, Subservice name: training and Service User: training-user

5. Save the changes. The properties are saved.

6. In Eclipse, go to training.ui.apps.

Extend and Customize Adobe Experience Manager 203


7. Right-click training.ui.apps and select Sling > Import from Server.

8. Accept the default values and click Finish. The changes are imported from server, as
shown:

ADOBE COPYRIGHT PROTECTED


9. Navigate to the Web Console, and choose Status > Sling Service User Mappings. The
user mappings are displayed.

Extend and Customize Adobe Experience Manager 204


10. Do a browser find: training and observe the new user mapping, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: You can find the Persistent Identity (PID) in the Web Console configuration console by
searching Service User. The -training is a unique identifier because this configuration is created from
a configuration factory and, therefore, it needs a unique identifier.
NOTE: You can create a node with the serialized XML document instead. Working with XMLs (that
represent nodes) outside of AEM is a typical developer practice in an IDE. The XML file named
org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-training.xml is
provided as part of /Exercise_Files/09_Sling_Web_Framework.

Extend and Customize Adobe Experience Manager 205


References
For further information on OSGi Configurations and Run Modes, visit:
• Apache Sling: https://sling.apache.org/

• Apache Felix: https://felix.apache.org/

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 206


Module 10: Access DataLayer in Adobe Experience
Manager using Sling

Introduction

Sling Models provide excellent support for web content authors to build pages in Adobe Experience
Manager (AEM) and to easily customize the pages in AEM to their requirements. Sling Models let AEM
developers access Sling content without creating their own adapters. Thus avoiding a large amount of

ADOBE COPYRIGHT PROTECTED


boilerplate code.

Objectives

• Describe ResourceResolver

• Describe Sling Models

• Create a Sling Model

• Discuss Sling Model Exporter in AEM

• Extend a core component

• Discuss Query Index

• Describe Index configuration

• Configure queries to search resources

Extend and Customize Adobe Experience Manager 207


Understanding ResourceResolver
The ResourceResolver defines the service API which may be used to resolve resource objects. The
resource resolver is available to the request processing servlet through the
SlingHttpServletRequest.getResourceResolver() method. A resource resolver can also be created
through the ResourceResolverFactory.
The ResourceResolver is also adaptable to get adapters to other types. A JCR based resource
resolver might support adapting to the JCR Session used by the resolver to access the JCR
Repository.
A ResourceResolver is generally not thread safe. As a consequence, an application that uses the
resolver, its returned resources and objects resulting from adapting either the resolver or a
resource, must provide proper synchronization to ensure not more than one thread concurrently

ADOBE COPYRIGHT PROTECTED


operates against a single resolver, resource, or resulting objects.Maven projects are configured
using a Project Object Model (POM) stored in a pom.xml file.
Accessing Resources

The ResourceResolver interface defines two kinds of methods to access resources—the resolve
method and the getResource method. The difference lies in the algorithm applied to find the
requested resource, and in the behavior in case a resource cannot be found.
Lifecycle

A Resource Resolver has a life cycle which begins with the creation of the Resource Resolver using
any of the factory methods and ends with calling the close() method. It is very important to call the
close() method after the resource resolver is not used anymore to ensure the system resources are
properly cleaned up.
To check whether a Resource Resolver can still be used, the isLive() method can be used.

Extend and Customize Adobe Experience Manager 208


Adapting Resources

An adapter design pattern is used when you want two different classes with incompatible
interfaces to work together. Sling offers an adapter pattern to conveniently translate objects that
implement the Adaptable interface. This interface provides a generic adaptTo() method that will
translate the object to the class type being passed as the argument.
The Resource and Resource Resolver interfaces are defined with a method adaptTo()which adapts
the object to other classes. The adaptTo pattern is used to adapt two different interfaces (for
example, resource to node).
Node node = resource.adaptTo(Node.class)
Using this mechanism, the JCR session of the Resource Resolver calls the adaptTo method with the

ADOBE COPYRIGHT PROTECTED


javax.jcr.Session class object. Likewise, the JCR node on which a resource is based can be retrieved
by calling the Resource.adaptTo method with the javax.jcr.Node class object.

Adaptable.adaptTo() can be implemented in multiple ways:


• By the object itself, implementing the method, and mapping to certain objects

• By an AdapterFactory that can map arbitrary objects

• A combination of both

Extend and Customize Adobe Experience Manager 209


Working with Sling Models
Introduced with Sling 7 (2014), Sling Models allow to define Java model objects (classes or
interfaces) and map those objects to Sling resources (like a JCR data structure or a
SlingHttpServletRequest), or even to OSGi services, expanding the use of the adaptTo()
method to any Java object.
A Sling Model can be seen as an abstraction layer that allows AEM components to consume
back-end logic, just like the JS-Use, WCMUsePojo or Sling Resource APIs would, but for
rather complex components with the need for reusability.
Using Sling Models

A Sling Model is a Java class located in an OSGi bundle that is annotated with @Model and

ADOBE COPYRIGHT PROTECTED


the adaptable class, for example, @Model(adaptables = Resource.class) for a resource.

In the example below, data members (the fields of the Java class) are annotated with @inject
to map to node properties.

Benefits of Using Sling Models


• Saves time from creating own adapters (avoiding boilerplate code)

• Supports both classes and interfaces

• Allows you to adapt multiple objects—minimal required are Resource and SlingHttpServletRequest

• Works with existing Sling infrastructure (for example, changes to other bundles are not required)

• Provides the ability to mock dependencies with tools like Mockito @InjectMocks

Extend and Customize Adobe Experience Manager 210


Sling Model Implementation
Sling Models allow developers to inject method return values (in an interface) and fields (in
a class) based on Sling resources and OSGi services. Most injections are available when
adapting either a Resource or a SlingHttpServletRequest object.
In most cases, using a Sling Model Interface requires less code to accomplish the same task.
In the case of Sling Model classes, the most common case where you would want to use
them is if you need to do any filtering or customization of the values being returned. When
using a class for your model, you can inject the values into the fields and generate a
formatted return value.
In the simplest case, the Java class is annotated with @Model and the adaptable class. Fields
that need to be injected are annotated with @Inject, as shown:

ADOBE COPYRIGHT PROTECTED


1. @Model(adaptables=Resource.class)
2. public class MyModel {

4. @Inject
5. private String propertyName;

In this case, a property named propertyName will be looked up from the Resource (after first
adapting it to a ValueMap) and will be injected.
For an interface, it is similar:

1. @Model(adaptables=Resource.class)
2. public interface MyModel {

4. @Inject
5. String getPropertyName();
6. }

Client code does not need to be aware that Sling Models is being used. It just uses the Sling
Adapter framework:
MyModel model = resource.adaptTo(MyModel.class)

If the field or method name does not exactly match the


1. @Model(adaptables=Resource.class)
2. public class MyModel {

4. @Inject @Named("secondPropertyName")
5. private String otherName;
6. }

Extend and Customize Adobe Experience Manager 211


Annotation Usage:

The following table shows the different types of Sling Model annotations and its usage:

Sling Model Code Snippet Injector


Annotation

@Model @Model(adaptables = Resource.class) Class is annotated with @Model and


the adaptable class

@Inject @Inject private String propertyName; (class) A property named propertyName will
@Inject String getPropertyName(); (interface) be looked up from the Resource
(after first adapting it to a ValueMap)
and it is injected. If property is not

ADOBE COPYRIGHT PROTECTED


found, it will return null.

@Default @Inject @Default(values=”AEM”) private A default value (for Strings, Arrays &
String technology; primitives)

@Optional @Inject @Optional private String otherName @Injected fields/methods are


assumed to be required. To mark
them as optional, use @Optional, for
example, resource or adaptable may
or may not have property.

@Named @Inject @Named(“title”) private String Inject a property whose name does
page Title; not match the Model field name.

@Via @Model(adaptables=SlingHttpServletReque Use a JavaBean property of the


st. class) adaptable as the source of the
Public interface SlingModelDemo { injection.
@Inject @Via(“resource”)
String getPropertyName(); } //Code snippet will return
request.getResource().adaptTo(Value
Map.class).get(“propertyName”,
String.class)
@Source @Model(adaptables=SlingHttpServletReque Explicitly tie an injected field or
st. class) method to a particular injector (by
@Inject @Source(“script-bindings”) name).
Resource getResource();
//Code snippet will ensure that
resource is retrieved from the
bindings, not a request attribute.
@PostConstruct @PostConstruct Allows for the definition of methods
protected void sayHello() { to be executed after the instance has
logger.info(“hello”); been instantiated, and all the injects
} have been performed.

Extend and Customize Adobe Experience Manager 212


Injector Specific Annotations
Sling Models are easily extensible, with custom injectors and annotations. To create a custom
injector, simply implement the org.apache.sling.models.spi.Injector interface and register your
implementation with the OSGi service registry. Here is a list of the available injectors:
https://sling.apache.org/documentation/bundles/models.html#available-injectors
Sometimes, it is necessary to use customized annotations that aggregate the standard annotations
described above. This generally has the following advantages over using the standard annotations:
• Less code to write (only one annotation is necessary in most of the cases)

• More robust (in case of name collisions among the different injectors, you must ensure the

ADOBE COPYRIGHT PROTECTED


right injector is used)

• Better IDE support

The following annotations are provided, which are tied to specific injectors:

Annotation Supported Optional Elements Injector

@ScriptVariable optional and name script-bindings

@ValueMapValue optional, name and via valuemap

@ChildResource optional, name and via child-resources

@RequestAttribute optional, name and via request-attributes

@ResourcePath optional, path, and name resource-path

@OSGiService optional, filter osgi-services

@Self optional self

@SlingObject optional sling-object

Extend and Customize Adobe Experience Manager 213


Injectors Lookup in Web Console

A list of the available injectors can be seen at: http://localhost:4502/system/console/status-slingmodels

Debugging

In order to see logging specific to Sling Models, add a logger for the package
org.apache.sling.models, set the log level to TRACE and dump the messages in to appropriate log
file.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 214


Exercise 1: Create a Sling Model
In this exercise, you will create a Sling Model as a Java class and map it into a resource that consists
of a set of properties on a JCR node in the repository. The Sling servlet performs the mapping,
which is used to return the values of the properties. In fact, you will use this class Model as a
wrapper to format the class fields, and display them in the browser.
This exercise includes three tasks:
1. Create data for the Sling Model
2. Adapt the resource to a Sling Model
3. Adapt the request to a Sling Model

ADOBE COPYRIGHT PROTECTED


Task 1: Create data for the Sling Model
This first task will create an adhoc data structure that our Sling Model will represent.

1. In CRXDE Lite, right-click content and select Create > Create Node. The Create Node dialog box
opens.

2. In the Create Node dialog box, carry out the following functions, as shown:

a. In the Name field, enter stocks.

b. Select sling:Folder from the Type drop-down menu.

3. Click OK. The node is created.

Extend and Customize Adobe Experience Manager 215


4. Click Save All.

5. Create a child node under stocks, carry out the following functions:

a. In the Name field, enter ADBE.

b. Select sling:OrderedFolder from the Type drop-down menu.

6. Click OK and select Save All to save the changes. The node ADBE is created.

ADOBE COPYRIGHT PROTECTED


7. Create a child node under ADBE, carry out the following functions:

a. In the Name field, enter lastTrade.

b. Select nt:unstructured from the Type drop-down menu.

8. Click OK. The node is created, as shown:

9. Click Save All.

Extend and Customize Adobe Experience Manager 216


10. In the newly created node, create the following four properties as follows:

• Name: lastTrade of type String and Value: 300

• Name: dayOfLastUpdate of type String and Value: 09/19/2018

• Name: companyName of type String and Value: Adobe Systems Incorporated

• Name: week52High of type String and Value: 310

ADOBE COPYRIGHT PROTECTED


11. Click Save All.

Extend and Customize Adobe Experience Manager 217


Task 2: Adapt the resource to a Sling Model
In this task you will adapt a sling resource to the Sling Model.

1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher
opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


4. Copy the StockModel.java file from /Exercises_Folder/10_Sling_Models.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/10_Sling_Models/.


Copy and paste the file from the exercise files referenced to StockModel.java to Eclipse. Please do not
copy the code from this exercise book. The code in the student guide is only for illustrative purposes.

5. Right-click com.adobe.training.core.models and paste the file. The StockModel.java class is


created, as shown:

Extend and Customize Adobe Experience Manager 218


6. Double-click StockModel.java to open it in editor, as shown:

41. package com.adobe.training.core.models;


42.
43. import javax.inject.Named;
44.
45. import org.apache.sling.api.resource.Resource;
46. import org.apache.sling.api.resource.ValueMap;
47. import org.apache.sling.models.annotations.DefaultInjectionStrategy;
48. import org.apache.sling.models.annotations.Model;
49. import org.apache.sling.models.annotations.injectorspecific.ChildResource;
50. import org.apache.sling.models.annotations.injectorspecific.Self;
51.
52. /**
53. * This model represents an IEX api stock data structure created from the StockDataImporter:

ADOBE COPYRIGHT PROTECTED


54. * /content/stocks/
55. * + <STOCK_SYMBOL> [sling:OrderedFolder]
56. * + lastTrade [nt:unstructured]
57. * - companyName = <value>
58. * - sector = <value>
59. * - lastTrade = <value>
60. * - ..
61. */
62.
63. @Model(adaptables=Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
64. public class StockModel{
65.
66. @Self
67. private Resource stock;
68.
69. @ChildResource
70. private Resource lastTrade;
71.
72. @ChildResource
73. @Named("lastTrade")
74. private ValueMap lastTradeValues;
75.
76.
77.
78. //Uses the resource to read the stock symbol
79. public String getSymbol() {
80. return stock.getName();
81. }
82.
83. // uses the ValueMap to read the stock data
84. public double getLastTrade() {
85. return lastTradeValues.get("lastTrade", Double.class);
86. }
87. public String getRequestDate() {
88. return lastTradeValues.get("dayOfLastUpdate", String.class);
89. }
90. public String getRequestTime() {
91. return lastTradeValues.get("timeOfUpdate", String.class);
92. }
93. public double getUpDown() {

Extend and Customize Adobe Experience Manager 219


94. return lastTradeValues.get("upDown", Double.class);
95. }
96. public double getOpenPrice() {
97. return lastTradeValues.get("openPrice", Double.class);
98. }
99. public double getRangeHigh() {
100. return lastTradeValues.get("rangeHigh", Double.class);
101. }
102. public double getRangeLow() {
103. return lastTradeValues.get("rangeLow", Double.class);
104. }
105. public int getVolume() {
106. return lastTradeValues.get("volume", Integer.class);
107. }
108. public String getCompanyName() {
109. return lastTradeValues.get("companyName", String.class);

ADOBE COPYRIGHT PROTECTED


110. }
111. public String getSector() {
112. return lastTradeValues.get("sector", String.class);
113. }
114. public double get52weekHigh() {
115. return lastTradeValues.get("week52High", Double.class);
116. }
117. public double get52weekLow() {
118. return lastTradeValues.get("week52Low", Double.class);
119. }
120. public double getYtdPercentChange() {
121. return lastTradeValues.get("ytdPercentageChange", Double.class);
122. }
123. }

NOTE: This StockModel will work with the StockDataImporter that you will create later in
this course. Because this data importer is not yet created, you will use a servlet and dummy
data to simulate the input and output for the StockModel.

7. Copy the SlingModelServlet.java file from Exercises_Folder/10_Sling_Models.

8. Right-click com.adobe.training.core.servlets and paste the file. The SlingModelServlet.java class is


created.

9. Double-click SlingModelServlet.java to examine the code:

1. package com.adobe.training.core.servlets;
2.
3. import java.io.IOException;
4.
5. import javax.servlet.Servlet;
6. import javax.servlet.ServletException;
7.
8. import org.osgi.service.component.annotations.Component;

Extend and Customize Adobe Experience Manager 220


9.
10. import org.apache.sling.api.SlingHttpServletRequest;
11. import org.apache.sling.api.SlingHttpServletResponse;
12. import org.apache.sling.api.resource.Resource;
13. import org.apache.sling.api.resource.ResourceResolver;
14. import org.apache.sling.api.resource.ValueMap;
15. import org.apache.sling.api.servlets.SlingAllMethodsServlet;
16.
17. import org.slf4j.Logger;
18. import org.slf4j.LoggerFactory;
19.
20. import com.adobe.training.core.models.StockModel;
21. //import com.adobe.training.core.models.StockModelAdaptFromRequest;
22.
23. /**
24. * Example URI http://localhost:4502/content/trainingproject/en.model.html/content/stocks/ADBE

ADOBE COPYRIGHT PROTECTED


25. *
26. * To use this servlet, a content structure must be created:
27. * /content/stocks/
28. * + ADBE [sling:OrderedFolder]
29. * + lastTrade [nt:unstructured]
30. * - lastTrade = "300"
31. * - dayOfLastUpdate = "11/13/2016"
32. * - companyName = "Adobe Systems Incorporated"
33. * - week52High = "310"
34. */
35.
36. @Component(service = Servlet.class,
37. property = {
38. "sling.servlet.resourceTypes=trainingproject/components/structure/page",
39. "sling.servlet.selectors=model"
40. })
41.
42. public class SlingModelServlet extends SlingAllMethodsServlet{
43. private final Logger logger = LoggerFactory.getLogger(this.getClass());
44.
45. private static final long serialVersionUID = 1L;
46.
47. @Override
48. public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws ServletException,IOExc
eption{
49. response.setContentType("text/html");
50. try
51. // Get the resource (a node in the JCR) using ResourceResolver from the request
52. (ResourceResolver resourceResolver = request.getResourceResolver()) {
53.
54. //Specify the node of interest in the suffix on the request
55. String nodePath = request.getRequestPathInfo().getSuffix();
56. if(nodePath != null){
57. Resource resource = resourceResolver.getResource(nodePath);
58.
59. // Adapt resource properties to variables using ValueMap, and log their values
60. Resource parent = resource.getChild("lastTrade");
61. ValueMap valueMap=parent.adaptTo(ValueMap.class);
62. response.getOutputStream().println("<h3>");
63. response.getOutputStream().println(resource.getName() + " lastTrade node with ValueMap is");

Extend and Customize Adobe Experience Manager 221


64. response.getOutputStream().println("</h3><br />");
65. response.getOutputStream().println("(Last Trade) "+ valueMap.get("lastTrade").toString() + " (Requested Day) "
+ valueMap.get("dayOfLastUpdate").toString()
66. + " (Company) " + valueMap.get("companyName").toString() + " (52 week High) " + valueMap.get("week52
High").toString());
67.
68. //Adapt the resource to our model (part 1)
69. StockModel stockModel = resource.adaptTo(StockModel.class);
70. response.getOutputStream().println("<br /><h3>");
71. response.getOutputStream().println(stockModel.getSymbol() + " lastTrade node with StockModel is");
72. response.getOutputStream().println("</h3><br />");
73. response.getOutputStream().println("(Last Trade) " + stockModel.getLastTrade() + " (Requested Day) " + stockM
odel.getRequestDate()
74. + " (Company) " + stockModel.getCompanyName() + " (52 week High) " + stockModel.get52weekHigh());
75.
76. //Adapt the request object to our model (part 2)

ADOBE COPYRIGHT PROTECTED


77. /*StockModelAdaptFromRequest stockModelAdaptFromRequest = request.adaptTo(StockModelAdaptFromReq
uest.class);
78. response.getOutputStream().println("<br /><h3>");
79. response.getOutputStream().println(stockModelAdaptFromRequest.getSymbol() + " lastTrade node with StockM
odelAdaptFromRequest is");
80. response.getOutputStream().println("</h3><br />");
81. response.getOutputStream().println("(Last Trade) " + stockModelAdaptFromRequest.getLastTrade() + " (Request
ed Time) " + stockModel.getRequestDate()
82. + " (Company) " + stockModelAdaptFromRequest.getCompanyName() + " (52 week High) " + stockModelAda
ptFromRequest.get52weekHigh());*/
83. }
84. else {
85. response.getWriter().println("Can't get the last trade node, enter a suffix in the URI");
86. }
87. } catch (Exception e) {
88. response.getWriter().println("Can't read last trade node. make sure the suffix path exists!");
89. logger.error(e.getMessage());
90. }
91. response.getWriter().close();
92. }
93. }

10. Right-click training.core, and perform Run As > Maven install to build the project. The project is
built.

Extend and Customize Adobe Experience Manager 222


11. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


12. Go to the following URL:

http://localhost:4502/content/trainingproject/en.model.html/content/stocks/ADBE

13. Verify the message returned by the model is displayed in the browser, as shown:

Extend and Customize Adobe Experience Manager 223


Task 3: Adapt the request to a Sling Model

In this task you will adapt the request rather than the resource to get the same outcome.

1. Copy the StockModelAdaptFromRequest.java file from /Exercises_Folder/10_Sling_Models.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/10_Sling_Models/.


Copy and paste the file from the exercise files referenced to StockModelAdaptFromRequest.java to
Eclipse. Please do not copy the code from this exercise book. The code in the student guide is only
for illustrative purposes.

ADOBE COPYRIGHT PROTECTED


2. Right-click com.adobe.training.core.models and paste the file. The StockModelAdaptFromRequest.java
class is created.

3. Double-click StockModelAdaptFromRequest.java to open it in editor, as shown:

1. package com.adobe.training.core.models;
2.
3. import org.apache.sling.api.SlingHttpServletRequest;
4. import org.apache.sling.api.resource.Resource;
5. import org.apache.sling.api.resource.ValueMap;
6. import org.apache.sling.models.annotations.DefaultInjectionStrategy;
7. import org.apache.sling.models.annotations.Model;
8. import org.apache.sling.models.annotations.injectorspecific.ResourcePath;
9. import org.apache.sling.models.annotations.injectorspecific.Self;
10.
11. import javax.annotation.PostConstruct;
12.
13. /**
14. * This model represents an IEX api stock data structure created from the StockDataImporter:
15. * /content/stocks/
16. * + <STOCK_SYMBOL> [sling:OrderedFolder]
17. * + lastTrade [nt:unstructured]
18. * - companyName = <value>
19. * - sector = <value>
20. * - lastTrade = <value>
21. * - ..
22. */
23.
24. @Model(adaptables=SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
25. public class StockModelAdaptFromRequest {
26.
27. @Self
28. private SlingHttpServletRequest request;
29.
30. @ResourcePath(path = "/")

Extend and Customize Adobe Experience Manager 224


31. private Resource root;
32.
33. private ValueMap lastTradeValues;
34. private Resource stock;
35.
36. @PostConstruct
37. private String stockContentPath() {
38. String path = request.getRequestPathInfo().getSuffix();
39. stock = root.getChild(path);
40. lastTradeValues = root.getChild(path).getChild("lastTrade").getValueMap();
41. return path;
42. }
43. //Uses the resource to read the stock symbol
44. public String getSymbol() {
45. return stock.getName();
46. }

ADOBE COPYRIGHT PROTECTED


47. // uses the ValueMap to read the stock data
48. public double getLastTrade() {
49. return lastTradeValues.get("lastTrade", Double.class);
50. }
51. public String getRequestDate() {
52. return lastTradeValues.get("dayOfLastUpdate", String.class);
53. }
54. public String getRequestTime() {
55. return lastTradeValues.get("timeOfUpdate", String.class);
56. }
57. public double getUpDown() {
58. return lastTradeValues.get("upDown", Double.class);
59. }
60. public double getOpenPrice() {
61. return lastTradeValues.get("openPrice", Double.class);
62. }
63. public double getRangeHigh() {
64. return lastTradeValues.get("rangeHigh", Double.class);
65. }
66. public double getRangeLow() {
67. return lastTradeValues.get("rangeLow", Double.class);
68. }
69. public int getVolume() {
70. return lastTradeValues.get("volume", Integer.class);
71. }
72. public String getCompanyName() {
73. return lastTradeValues.get("companyName", String.class);
74. }
75. public String getSector() {
76. return lastTradeValues.get("sector", String.class);
77. }
78. public double get52weekHigh() {
79. return lastTradeValues.get("week52High", Double.class);
80. }
81. public double get52weekLow() {
82. return lastTradeValues.get("week52Low", Double.class);
83. }
84. public double getYtdPercentChange() {
85. return lastTradeValues.get("ytdPercentageChange", Double.class); } }

Extend and Customize Adobe Experience Manager 225


NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/10_Sling_Models/.
Copy and paste the file from the exercise files referenced to SlingModelServlet_P2.java to Eclipse.
Please do not copy the code from this exercise book. The code in the student guide is only for
illustrative purposes.

4. Edit SlingModelServlet.java class by uncommenting the following lines of code, as shown:

1. package com.adobe.training.core.servlets;
2.
3. import java.io.IOException;
4.
5. import javax.servlet.Servlet;
6. import javax.servlet.ServletException;

ADOBE COPYRIGHT PROTECTED


7.
8. import org.osgi.service.component.annotations.Component;
9.
10. import org.apache.sling.api.SlingHttpServletRequest;
11. import org.apache.sling.api.SlingHttpServletResponse;
12. import org.apache.sling.api.resource.Resource;
13. import org.apache.sling.api.resource.ResourceResolver;
14. import org.apache.sling.api.resource.ValueMap;
15. import org.apache.sling.api.servlets.SlingAllMethodsServlet;
16.
17. import org.slf4j.Logger;
18. import org.slf4j.LoggerFactory;
19.
20. import com.adobe.training.core.models.StockModel;
21. import com.adobe.training.core.models.StockModelAdaptFromRequest;
22.
23. /**
24. * Example URI http://localhost:4502/content/trainingproject/en.model.html/content/stocks/ADBE
25. *
26. * To use this servlet, a content structure must be created:
27. * /content/stocks/
28. * + ADBE [sling:OrderedFolder]
29. * + lastTrade [nt:unstructured]
30. * - lastTrade = "300"
31. * - dayOfLastUpdate = "11/13/2016"
32. * - companyName = "Adobe Systems Incorporated"
33. * - week52High = "310"
34. */
35.
36. @Component(service = Servlet.class,
37. property = {
38. "sling.servlet.resourceTypes=trainingproject/components/structure/page",
39. "sling.servlet.selectors=model"
40. })
41.
42. public class SlingModelServlet extends SlingAllMethodsServlet{
43. private final Logger logger = LoggerFactory.getLogger(this.getClass());
44.
45. private static final long serialVersionUID = 1L;
46.

Extend and Customize Adobe Experience Manager 226


47. @Override
48. public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws ServletException,IO
Exception{
49. response.setContentType("text/html");
50. try
51. // Get the resource (a node in the JCR) using ResourceResolver from the request
52. (ResourceResolver resourceResolver = request.getResourceResolver()) {
53.
54. //Specify the node of interest in the suffix on the request
55. String nodePath = request.getRequestPathInfo().getSuffix();
56. if(nodePath != null){
57. Resource resource = resourceResolver.getResource(nodePath);
58.
59. // Adapt resource properties to variables using ValueMap, and log their values
60. Resource parent = resource.getChild("lastTrade");
61. ValueMap valueMap=parent.adaptTo(ValueMap.class);

ADOBE COPYRIGHT PROTECTED


62. response.getOutputStream().println("<h3>");
63. response.getOutputStream().println(resource.getName() + " lastTrade node with ValueMap is");
64. response.getOutputStream().println("</h3><br />");
65. response.getOutputStream().println("(Last Trade) "+ valueMap.get("lastTrade").toString() + " (Requested Day
) " + valueMap.get("dayOfLastUpdate").toString()
66. + " (Company) " + valueMap.get("companyName").toString() + " (52 week High) " + valueMap.get("wee
k52High").toString());
67.
68. //Adapt the resource to our model (part 1)
69. StockModel stockModel = resource.adaptTo(StockModel.class);
70. response.getOutputStream().println("<br /><h3>");
71. response.getOutputStream().println(stockModel.getSymbol() + " lastTrade node with StockModel is");
72. response.getOutputStream().println("</h3><br />");
73. response.getOutputStream().println("(Last Trade) " + stockModel.getLastTrade() + " (Requested Day) " + stoc
kModel.getRequestDate()
74. + " (Company) " + stockModel.getCompanyName() + " (52 week High) " + stockModel.get52weekHigh());
75.
76. //Adapt the request object to our model (part 2)
77. StockModelAdaptFromRequest stockModelAdaptFromRequest = request.adaptTo(StockModelAdaptFromRe
quest.class);
78. response.getOutputStream().println("<br /><h3>");
79. response.getOutputStream().println(stockModelAdaptFromRequest.getSymbol() + " lastTrade node with Sto
ckModelAdaptFromRequest is");
80. response.getOutputStream().println("</h3><br />");
81. response.getOutputStream().println("(Last Trade) " + stockModelAdaptFromRequest.getLastTrade() + " (Req
uested Time) " + stockModel.getRequestDate()
82. + " (Company) " + stockModelAdaptFromRequest.getCompanyName() + " (52 week High) " + stockModel
AdaptFromRequest.get52weekHigh());
83. }
84. else {
85. response.getWriter().println("Can't get the last trade node, enter a suffix in the URI");
86. }
87. } catch (Exception e) {
88. response.getWriter().println("Can't read last trade node. make sure the suffix path exists!");
89. logger.error(e.getMessage());
90. }
91. response.getWriter().close();
92. }
93. }

Extend and Customize Adobe Experience Manager 227


5. Click Save.

6. Right-click training.core, and perform Run As > Maven install to build the project. The build starts.

7. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


8. Go to the following URL:

http://localhost:4502/content/trainingproject/en.model.html/content/stocks/ADBE

9. Verify the message returned by the model is displayed in the browser, as shown:

NOTE: Notice that you can adapt the request rather than the resource to our StockModel. The object
you are adapting does not matter as long as the Sling Model is built to handle the object

Extend and Customize Adobe Experience Manager 228


Sling Model Exporter
Primarily used to easily expose data to HTL scripts with minimal code, Sling Models are becoming a major
development business object in AEM, especially with the recent introduction of model exporters in Sling 9
(2017).

The Sling Model Exporter feature allows new annotations to be added to Sling Models that define how the
Model can be exported as a different Java object, or more commonly, serialized into a different format
such as JSON, to be consumed by your front-end application (headless CMSs for example).

This feature is not available OOTB with the WCMUsePojo API (for which you would have to override the
activate() method), whereas with Sling Models the initialization is done by a method annotated with
@PostConstruct.

ADOBE COPYRIGHT PROTECTED


Apache Sling provides a Jackson JSON exporter to cover the most common case of exporting Sling Models
as JSON objects. The exporter scans through all the getters and serialize them into JSON. The model needs
to define the resourceType of the component as a modifier, in order to be bound to it.

The snippet below shows the declaration of a model for an AEM component, that gets exported with the
Jackson exporter using the specific component adapter interface
com.adobe.cq.export.json.ComponentExporter:
@Model(adaptables=SlingHttpServletRequest.class,

adapters= {ComponentExporter.class},

resourceType="trainingproject/components/content/trainingtitle",

defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)

@Exporter(name = "jackson", extensions = "json")

public class TrainingTitle implements ComponentExporter{

Extend and Customize Adobe Experience Manager 229


Exercise 2: Extend a core component
In this exercise, you will upload and install a trainingtitle component. This will be the frontend HTL
code for your component. You will then write a Sling Model to support the business logic.

1. From CRXDE Lite, click the Package icon. The CRX Package Manager page is displayed.

2. Click Upload Package, as shown. The Upload Package dialog box opens.

ADOBE COPYRIGHT PROTECTED


3. In the Upload Package dialog box, click Browse, and select the trainingtitle-component.zip package file
from the Exercise_Files\10_Sling_Models.

4. Click OK.

5. Verify the uploaded package is now available in CRX Package Manager, as shown:

6. Click Install. The package is installed.

Extend and Customize Adobe Experience Manager 230


7. In Eclipse Project Explorer, under training.ui.apps, select /src/main/content/jcr_root [rep:root], right-click
and select Import from Server. The Import from Repository dialog box appears.

8. Click Finish. The project is imported from the server.

9. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


10. Copy the TrainingTitle.java file from Exercises_Folder/10_Sling_Models.

11. Right-click com.adobe.training.core.models and paste the file. The TrainingTitle.java class is created, as
shown:

Extend and Customize Adobe Experience Manager 231


12. Double-click TrainingTitle.java to open it in editor, as shown:

1. package com.adobe.training.core.models;
2.
3. import javax.annotation.PostConstruct;
4. import javax.inject.Inject;
5. import javax.inject.Named;
6.
7. import org.apache.sling.api.SlingHttpServletRequest;
8. import org.apache.sling.models.annotations.*;
9. import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
10. import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;

ADOBE COPYRIGHT PROTECTED


11. import org.apache.sling.models.annotations.injectorspecific.Self;
12. import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
13. import org.slf4j.Logger;
14.
15. import com.adobe.cq.export.json.ComponentExporter;
16. import com.day.cq.wcm.api.Page;
17.
18. @Model(adaptables=SlingHttpServletRequest.class,
19. adapters= {ComponentExporter.class},
20. resourceType="trainingproject/components/content/trainingtitle",
21. defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
22. @Exporter(name = "jackson", extensions = "json")
23. public class TrainingTitle implements ComponentExporter{
24.
25. @Self
26. private SlingHttpServletRequest request;
27.
28. @Inject
29. @Named("log")
30. private Logger logger;
31.
32. @ScriptVariable
33. private Page currentPage;
34.
35. @ValueMapValue(name="subtitle", injectionStrategy=InjectionStrategy.OPTIONAL)
36. private String subtitle;
37.
38. @ValueMapValue(name="jcr:title", injectionStrategy=InjectionStrategy.OPTIONAL)
39. private String title;
40.
41. @ValueMapValue(name="type", injectionStrategy=InjectionStrategy.OPTIONAL)

Extend and Customize Adobe Experience Manager 232


42. private String type;
43.
44. @PostConstruct
45. protected void initModel(){
46. if(title == null){
47. title = "";
48. }
49.
50. if(subtitle == null){
51. subtitle = "";
52. }
53. logger.info("TrainingTitle InitModel");
54. }

ADOBE COPYRIGHT PROTECTED


55.
56. public String getText() {
57. return title;
58. }
59.
60. public String getType() {
61. return type;
62. }
63.
64. public String getSubtitle() {
65. return subtitle;
66. }
67.
68. @Override
69. public String getExportedType() {
70. return request.getResource().getResourceType();
71. }
72. }

13. Right-click training.core, and perform Run As > Maven install to build the project. The build starts.

Extend and Customize Adobe Experience Manager 233


14. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


15. Go the Sites Console http://localhost:4502/sites.html/content.

16. Navigate to Sites > TrainingProject Site, and click the thumbnail on English and select the page, as
shown:

17. Click Edit (e). The page opens in a new tab for editing.

Extend and Customize Adobe Experience Manager 234


18. In the left pane, select the Components tab. Use the Filter option to find Training Title component, as
shown:

ADOBE COPYRIGHT PROTECTED


19. Drag the Training Title on the Drag components here container, as shown:

20. Double-click the Training Title components box to open it.

Extend and Customize Adobe Experience Manager 235


21. Add a title and subtitle, as shown:

ADOBE COPYRIGHT PROTECTED


22. Click Done (the checkmark symbol). The changes are saved.

Extend and Customize Adobe Experience Manager 236


23. Observe the component exported as JSON by using the URL, as shown:
http://localhost:4502/content/trainingproject/en.model.json

ADOBE COPYRIGHT PROTECTED


Note this is a JSON for the entire page, but it contains the TrainingTitle sling model with the 3 getters.

Extend and Customize Adobe Experience Manager 237


24. View only the json output of the component by references the resource directly:

http://localhost:4502/content/trainingproject/en/jcr:content/root/responsivegrid/trainingtitle.model.json,
as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 238


Query Index
Apache Oak does not index content by default as Jackrabbit 2 does. You must create custom
indexes when necessary, much like in traditional RDBs. As a finished product, AEM ships with the
most common indexers for Apache Oak to make indexing an easier task. If there is no index for a
specific query, the repository will be traversed. That is, the query will still work, but will probably be
very slow.
If Oak encounters a query without an index, a WARN level log message displays:
*WARN* Traversed 1000 nodes with filter Filter(query=select...) consider creating an index or changing the
query
If this is the case, you might need to create an index, or you might need to change the condition of

ADOBE COPYRIGHT PROTECTED


the query to take advantage of an existing index.
NOTE: If a query reads more than 10,000 nodes in memory, the query is cancelled with an
UnsupportedOperationException saying that, The query read more than 10000 nodes in memory. To
avoid running out of memory, processing was stopped. As a workaround, you can change this limit
by using the system property oak.queryLimitInMemory.
Query Indices are defined under the oak:index node.
Supported Query Languages
The Oak query engine supports the following languages:
• XPath

• SQL

• SQL-2

Extend and Customize Adobe Experience Manager 239


Oak Query Implementation Overview
The new Apache Oak based backend allows different indexers to be plugged into the repository.
The standard indexer is the Property Index, for which the index definition is stored in the repository
itself. External full text indexers can also be used with AEM. Implementations for Apache Lucene
and Solr are available by default. The Traversal Index indexer is the one used if no other indexer is
available. This means the content is not indexed, and all the content nodes are traversed to find
matches to the query. If multiple indexers are available for a query, each available indexer
estimates the cost of executing the query. Oak then chooses the indexer with the lowest estimated
cost.

ADOBE COPYRIGHT PROTECTED


The above diagram is a high-level representation of the query execution mechanism of Apache
Oak.
First, the query is parsed into an Abstract Syntax Tree, then the query is checked and transformed
into SQL-2, which is the native language for Oak queries.
After the query is checked and transformed into SQL-2, each index is consulted to estimate the cost
for the query. After completed, the results from the most cost-effective index are retrieved. Finally,
the results are filtered, to ensure the current user has read access to the result and the result
matches the complete query.

Extend and Customize Adobe Experience Manager 240


Query Processing on Cost Calculations

Internally, the query engine uses a cost-based query optimizer that asks all the available query indexes for
the estimated cost to process the query. It then uses the index with the lowest cost.
By default, the following indexes are available:
• Property index for each indexed property

• Full-text index, which is based on Apache Lucene/Solr

• Node type index, which is based on an property index for the properties jcr:primaryType and
jcr:mixins

ADOBE COPYRIGHT PROTECTED


• Traversal index that iterates over a subtree

If no index can efficiently process the filter condition, the nodes in the repository are traversed at
the given subtree. Usually, data is read from the index and repository while traversing over the
query result. There are exceptions, however, where all the data is read in memory when using a
full-text index and when using an order by clause.
Native Queries

To take advantage of features available in full-text index implementations, such as Apache Lucene
and Apache Lucene Solr, the so-called native constraints are supported by Oak. These constraints
are passed directly to the full-text index. The
full-text index is supported for both XPath and SQL-2. For XPath queries, the name of the function
is rep:native, and for SQL-2, it is native.
The first parameter is the index type and currently supported parameters are Solr and Lucene. The
second parameter is the native search query expression.
For SQL-2, the selector name (if needed) is the first parameter, just before the language. If full-text
implementation is not available, the queries will fail.

Extend and Customize Adobe Experience Manager 241


Configuring the Indexes
Indexes are configured as nodes in the repository under the oak:index node. The type of the index node must
be oak:QueryIndexDefinition. Several configuration options are available for each indexer as node properties.
For more information, see the following configuration details for Property Index, Lucene Index, and Solr
Index.
Configuring the Property Index

To configure the Property Index:


1. Open CRXDE Lite by going to http://localhost:4502/crx/de/index.jsp

2. Create a new node under oak:index.

ADOBE COPYRIGHT PROTECTED


3. Name the node PropertyIndex, and set the node type to oak:QueryIndexDefinition.

4. Click OK then set the following properties for the new node:

Name Type Value


type String property
propertyNames Name jcr:uuid
The above node will index the jcr:uuid property, whose job is to expose the universally unique
idetifier (UUID) of the node it is attached to.
5. Click Save All to save the changes—the PropertyIndex node will have three properties on its tab
now.

The Lucene Full-text Index

A full-text indexer based on Apache Lucene is available in AEM. If a full-text index is configured, then all the
queries with a full-text condition use the full-text index, no matter if there are other conditions that are
indexed and there is a path restriction.
If no full-text index is configured, then queries with full-text conditions may not work as expected. The query
engine has a basic verification in place for full-text conditions, but it does not support all the features that
Lucene does, and it traverses all the nodes if there are no indexed constraints.
As the index is updated through an asynchronous background thread, some full-text searches will be
unavailable for a small window of time until the background processes are finished.

Extend and Customize Adobe Experience Manager 242


You can configure a Lucene full-text index using the following procedure:
1. Open CRXDE Lite and create a new node under oak:index.

2. Name the node LuceneIndex and set the node type to oak:QueryIndexDefinition.

3. Add the following properties to the node:

Name Type Value


type String lucene
async String async
4. Save the changes.

ADOBE COPYRIGHT PROTECTED


Configuring the Solr Index

You can use the Solr index for full-text search, as well as search by path, property restrictions, and primary
type restrictions.
• It can be used for any type of JCR query.

• Its integration with AEM occurs at the repository level.

• It can be configured to work as an embedded server with the AEM instance, or as a remote server.

To configure AEM with an embedded Solr server:


5. Navigate to the Web Console at: http://localhost:4502/system/console/configMgr

6. Search for Oak Solr server provider in the Web Console.

7. Click the Edit icon, and in the resulting dialog box, select the server type as Embedded Solr from
the drop-down list.

8. Click Save.

9. Search for Oak Solr embedded server configuration in the Web Console under OSGi >
Configuration.

10. Click the Edit icon in the Oak Solr embedded server configuration, and update the configuration.
The Solr home directory configuration will look for a folder with the same name in the AEM
installation folder.

11. Open CRXDE Lite, and log in as admin.

Extend and Customize Adobe Experience Manager 243


12. Under oak:index, create a node called solrIndex, of type oak:QueryIndexDefinition, with the
following three properties:

• Name: type, Type: String, Value: solr

• Name: async, Type: String, Value: async

• Name: reindex, Type: Boolean, Value: true

13. Click Save All in the upper-left corner.

Temporarily Disabling an Index

When removing an index, it is always recommended to temporarily disable the index by setting
the type property to disabled and do testing to ensure that your application functions correctly

ADOBE COPYRIGHT PROTECTED


before actually deleting it.
Indexing Tools

AEM also integrates two indexing tools present in AEM as part of the Adobe Consulting Services
Commons toolset.
• Explain Query: A tool designed to help administrators understand how queries are executed.

• Oak Index Manager: A Web User Interface for maintaining existing indexes.

Explain Query Tool


This is a tool designed to help administrators understand how queries are executed. Oak attempts to figure
out the best way to execute any given query, based on the Oak indexes defined in the repository under the
oak:index node. Depending on the query, different indexes may be chosen by Oak. Understanding how Oak
is executing a query is the first step to optimizing the query.
Features of the Query Tool :
• Supports the Xpath, JCR-SQL, and JCR-SQL2 query languages

• Reports the actual execution time of the provided query

• Detects slow queries and warns about queries that could be potentially slow

• Reports the Oak index used to execute the query

• Displays the actual Oak Query engine explanation

Extend and Customize Adobe Experience Manager 244


• Provides click-to-load list of Slow and Popular queries, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 245


The first entry in the Query Explanation section is the actual explanation. The explanation will show the type
of index that was used to execute the query.
The second entry is the query plan. Selecting the Include execution time checkbox before running the query
will also show the amount of time the query was executed.
The tool will also build a list of popular and slow queries that can be automatically loaded in the UI
by selecting their name in the predefined list.
Oak Index Manager

The Oak Index Manager is a simple Web UI created to facilitate index management, such as
maintaining indexes, viewing their status, or triggering re-indexes. You can use the UI to filter
indexes in the table by typing in the filter criteria in the search box in the upper-left corner of the
screen.

ADOBE COPYRIGHT PROTECTED


You can trigger a single reindex by clicking the icon in the Reindex column for the respective index.
You can trigger a bulk reindex by selecting multiple indexes and clicking the Bulk Reindex button.
You can access the Explain Query and Oak Index Manager tools by clicking Tools > Operations >
Dashboard > Diagnosis.

Query Syntax
As specified, JSR 170: SQL and XPath syntaxes have the same feature set. Since JSR-283, Abstract Query
Model (AQM) defines the structure and semantics of a query. The specification defines two language
bindings for AQM:
• JCR-SQL2 (Grammar: http://www.h2database.com/jcr/grammar.html)

• JCR-JQOM

Basic AQM Concepts

A query has one or more selectors. When the query is evaluated, each selector independently selects a
subset of the nodes in the workspace based on Node type.
Join transforms the multiple sets of nodes selected by each selector into a single set. The join type can be
inner, left-outer, or right-outer.
AQM Concepts: Constraints

A query can specify a constraint to filter the set of node-tuples. The constraints may be any combination of:
• Absolute or relative path. For example, nodes that are children of /pictures

• Name of the node

• Value of a property. Example of value of a property—nodes whose jcr:created property is after


2007-03-14T00:00:00.000Z.

• Length of a property. Example of length of a property—nodes whose jcr:data property is longer


than 100 KB.

Extend and Customize Adobe Experience Manager 246


• Existence of a property. Example of existence of a property—nodes with a jcr:description property.

• Full-text search. For example, nodes that have a property containing the phrase beautiful sunset.

Search Basics
Defining and executing a JCR-level search requires the following logic:

QueryManager qm = session.getWorkspace().getQueryManager();
• Get the QueryManager for the Session/Workspace:

Query q = qm.createQuery(“select * from moviedb:movie order by Title”,Query.SQL);

ADOBE COPYRIGHT PROTECTED


• Create the query statement:

• Execute the query:


QueryResult res = q.execute();
• Iterate through the results:

NodeIterator nit = res.getNodes();


Query Examples—SQL2
• Find all nt:folder nodes.

SELECT * FROM [nt:folder]


• Find all files under /var. Exclude files under /var/classes.

SELECT * FROM [nt:file] AS files


WHERE ISDESCENDANTNODE(files, [/var])
AND (NOT ISDESCENDANTNODE(files, [/var/classes]))
• Find all files under /var (but not under /var/classes) created by existing users. Sort the results in
ascending order by jcr:createdBy and jcr:created.

SELECT * FROM [nt:file] AS file


INNER JOIN [rep:User] AS user ON file.[jcr:createdBy] = user.
[rep:principalName]
WHERE ISDESCENDANTNODE(file, [/var])
AND (NOT ISDESCENDANTNODE(file, [/var/classes]))
ORDER BY file.[jcr:createdBy], file.[jcr:created]

Extend and Customize Adobe Experience Manager 247


Search Performance

Fastest JCR search methodologies:


• Constraints on properties, Node types, full-text

• Typically O(n), where n is the number of results, versus to the total number of nodes

• Constraints on the path

JCR methodologies that need some planning:


• Constraints on the child axis

• Sorting, limit/offset

ADOBE COPYRIGHT PROTECTED


• Joins

Testing Queries

You can test your queries using CRXDE Lite. Access the Query Tool on the Tools menu from the top toolbar
in CRXDE Lite, as shown:

Extend and Customize Adobe Experience Manager 248


Exercise 3: Search resources with queries
In this exercise, you will do SQL query to search for a full text phrase in a website and then display the
pages that contain that phrase.

1. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


2. Copy the SearchServlet.java file from /Exercises_Folder/10_Sling_Models.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/10_Sling_Models/.


Copy and paste the file from the exercise files referenced to SearchServlet.java to Eclipse. Please do
not copy the code from this exercise book. The code in the student guide is only for illustrative
purposes.

3. Right-click com.adobe.training.core.servlets and paste the file. The SearchServlet.java class is created,
as shown:

Extend and Customize Adobe Experience Manager 249


4. Double-click SearchServlet.java to open it in editor, as shown:

1. package com.adobe.training.core.servlets;
2.
3. import com.day.cq.wcm.api.PageManager;
4. import com.google.gson.Gson;
5. import org.apache.sling.api.SlingHttpServletRequest;
6. import org.apache.sling.api.SlingHttpServletResponse;
7. import org.apache.sling.api.resource.Resource;
8. import org.apache.sling.api.resource.ResourceResolver;
9. import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
10.
11. import org.osgi.service.component.annotations.Component;
12.
13. import javax.jcr.query.Query;

ADOBE COPYRIGHT PROTECTED


14.
15. import javax.servlet.Servlet;
16. import javax.servlet.ServletException;
17. import java.io.IOException;
18. import java.util.ArrayList;
19. import java.util.Iterator;
20.
21. /**
22. *
23. * Example URI: http://localhost:4502/content/trainingproject/en.search.html?q=Lorem&wcmmode=disabled
24. *
25. */
26. @Component(service = Servlet.class,
27. property = {"sling.servlet.resourceTypes=trainingproject/components/structure/page",
28. "sling.servlet.selectors=search"})
29.
30. public class SearchServlet extends SlingSafeMethodsServlet {
31.
32. private static final long serialVersionUID = 3169795937693969416L;
33.
34. @Override
35. public final void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response)
36. throws ServletException, IOException {
37. response.setHeader("Content-Type", "application/json");
38.
39. ArrayList resultArray = new ArrayList();
40. Gson gson = new Gson();
41.
42. try (ResourceResolver rr = request.getResourceResolver()) {
43.
44. // Path of the node, which triggers this servlet
45. Resource requestingResource = request.getResource();
46.
47. // Then adapt the resource tree into a page tree
48.
49. PageManager pageManager = rr.adaptTo(PageManager.class);
50.
51. // Now we can find the page, that was initiating the search
52. String queryingPagePath = pageManager.getContainingPage(requestingResource).getPath();
53.

Extend and Customize Adobe Experience Manager 250


54. String queryTerm = (request.getParameter("q") != null) ? request.getParameter("q") : "";
55.
56. String QUERY_STRING = "SELECT * " +
57. "FROM [nt:unstructured] AS node " +
58. "WHERE ISDESCENDANTNODE([" + queryingPagePath + "]) " +
59. "and CONTAINS(node.*, '" + queryTerm + "')";
60.
61. Iterator<Resource> resourcesIterator = rr.findResources(QUERY_STRING, Query.JCR_SQL2);
62.
63. //Add the search results into the json array
64. while (resourcesIterator.hasNext()) {
65. Resource foundResource = resourcesIterator.next();
66. resultArray.add(foundResource.getPath());
67. }
68.
69. String json = gson.toJson(resultArray);

ADOBE COPYRIGHT PROTECTED


70.
71. response.getWriter().print(json);
72. response.getWriter().close();
73.
74. } catch (Exception e) {
75. e.printStackTrace();
76. }
77. }
78. }

5. Right-click training.core, and perform Run As > Maven install to build the project. The build starts.

6. Verify the bundle installed successfully, as shown:

Extend and Customize Adobe Experience Manager 251


7. Go to http://localhost:4502/content/trainingproject/en.search.html?q=Lorem&wcmmode=disabled
and verify the output, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 252


Module 11: Event Handling in Adobe Experience
Manager

Introduction

With AEM, there’s different approaches that can be taken regarding event management. Depending on
the complexity, the nature of the payload or intensity of process, inherent requirements or business roles

ADOBE COPYRIGHT PROTECTED


involved, we can consider the following techniques:

• Events at the OSGi container level, with the OSGi Event Admin

• Events at the Sling level, with Sling Jobs (that can be scheduled) and the ResourceChangeListener

• Events at the level of AEM, with different high-level objects e.g. workflow process steps, polling
importers, replication actions, notifications or auditing (all based on Sling Jobs)

• Events at the repository level, with JCR observation

Objectives

• Explain OSGi events

• Describe Sling Jobs

• Handle OSGi events

• Create Job consumer

• Describe Sling scheduler

• Clean up nodes with a Sling scheduler

• Discuss event modeling

• Create a resource listener

Extend and Customize Adobe Experience Manager 253


Sling Events
Events are used to trigger jobs or workflows. Apache Sling enables you to manage these events
within your application. Apache Sling provides support for event handling jobs, and scheduling.
Apache Sling’s event mechanism leverages the OSGi Event Admin Specification. The OSGi API for
events is simple and lightweight. Sending an event involves generating the event object and calling
the event admin. Receiving an event requires implementation of a single interface and a
declaration, through properties, on the interested topics. Each event is associated with an event
topic, and event topics are hierarchically organized.
The OSGi specification for event handling uses a publish or subscribe mechanism based on these
hierarchical topics. There is a loose coupling of the services, based on the whiteboard pattern.
When the publisher has an event object to deliver, it calls all the event listeners in the registry.

ADOBE COPYRIGHT PROTECTED


Various types of events can be handled by OSGi API:
a. Predefined events include Framework events. For example, a Bundle event, which indicates a change
in a bundle’s lifecycle.

b. Service events, which indicate a service lifecycle change

c. Configuration events, which indicate a configuration was updated or deleted.

d. JCR observation events

e. Application-generated events

f. Events from messaging systems (~JMS)

g. External events

Extend and Customize Adobe Experience Manager 254


Listening to OSGi Events
The OSGi specification defines an Event Admin Service mechanism, to manage event-based
communication between bundles. It’s based on the whiteboard pattern paradigm (publish /
subscribe), similarly to service injection, and can be performed either synchronously or
asynchronously.
The OSGi Event Admin defines two components to handle the sending and the reception of
events:
h. An Event Publisher, to send events based on a certain topic, with data associated to the event

i. An Event Handler, to listen to a certain topic and process the data carried by the event upon reception

ADOBE COPYRIGHT PROTECTED


Therefore, sending an event requires to register an event publisher, create an Event object and
specify the topic. Those events are then received by the OSGi Event Admin and distributed to
registered listeners, like described in the following picture:

Extend and Customize Adobe Experience Manager 255


A component that listens to OSGi events needs to:

a. Implement the EventHandler interface

b. Subscribe to service registration, with a property EventConstants.EVENT_TOPIC

c. Handle the Event object when it gets published

Here’s an example of a component registering a listener. Notice the immediate modifier set to true,
this is required here because we need the component to listen to events upon activation. The
method handleEvent() enables to consume the event with the Event object passed through it.

ADOBE COPYRIGHT PROTECTED


import org.osgi.service.event.EventHandler;

@Component (immediate = true,


Service = EventHandler.class,
Property = {Constants.SERVICE_DESCRIPTION + "=Replication
Listener kicks off a job",
EventConstants.EVENT_TOPIC + ReplicationAction.EVENT_TOPIC})

public class ReplicationListener implements EventHandler {


@override
public void handleEvent(final Event event) {

// do something

Limitations

This distribution of OSGi events is a broadcast, which means that the same event is being
consumed by all the components that had registered listeners against the same topic. If no listener
has been registered on that topic, the event is lost, hence won’t be handled later (it doesn’t get
queued). We’ll see later in this module that this “fire and forget” behavior can be avoided with the
use of events managed as jobs with Sling.

Extend and Customize Adobe Experience Manager 256


Exercise 1: Handle OSGi Events
In this exercise, you will write a class to handle events for the replication action, which might be a
publish or unpublish event. When the publish or unpublish action happens, this event handler will
be triggered.
This exercise includes two tasks:
1. Create a new Java class
2. Deploy the project
Task 1: Create a new Jav class

1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher

ADOBE COPYRIGHT PROTECTED


opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

Extend and Customize Adobe Experience Manager 257


4. Copy the file ReplicationListener.java from Exercise_Files/11_Event_Handling/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/11_Event_Handling/.


Copy and paste the file from the exercise files referenced to ReplicationListener.java to Eclipse. Please
do not copy the code from this exercise book. The code in the student guide is for illustrative purposes
only.

5. In Eclipse, right-click com.adobe.training.core.listeners and paste the file. The ReplicationListener.java


class is created, as shown:

ADOBE COPYRIGHT PROTECTED


6. Double-click ReplicationListener.java. The file opens in the editor.

1. package com.adobe.training.core.listeners;
2.
3. import java.util.HashMap;
4.
5. import org.osgi.service.component.annotations.Component;
6. import org.osgi.service.component.annotations.Reference;
7. import org.osgi.framework.Constants;
8. import org.osgi.service.event.Event;
9. import org.osgi.service.event.EventHandler;
10. import org.osgi.service.event.EventConstants;
11. import org.slf4j.Logger;
12. import org.slf4j.LoggerFactory;
13. import com.day.cq.replication.ReplicationAction;
14. import com.day.cq.replication.ReplicationActionType;
15.
16. import org.apache.sling.event.jobs.JobManager;
17.
18. @Component( immediate = true,
19. service = EventHandler.class,
20. property = {Constants.SERVICE_DESCRIPTION + "=Replication Listener kicks off a job",

21. EventConstants.EVENT_TOPIC + "=" + ReplicationAction.EVENT_TOPIC})


22.

Extend and Customize Adobe Experience Manager 258


23. public class ReplicationListener implements EventHandler {
24. private final Logger logger = LoggerFactory.getLogger(getClass());
25.
26. //dictates to the JobManager which JobConsumer will be instantiated in line 46
27. private static final String TOPIC = "com/adobe/training/core/replicationjob";
28.
29. @Reference
30. private JobManager jobManager;
31.
32. @Override
33. public void handleEvent(final Event event) {
34. ReplicationAction action = ReplicationAction.fromEvent(event);
35. logger.info("PATH: " + action.getPath().toString());

ADOBE COPYRIGHT PROTECTED


36. if (action.getType().equals(ReplicationActionType.ACTIVATE)) {
37.
38. if (action.getPath() != null)
39. {
40. try {
41. // Create a properties map that contains things we want to pass through the job
42. HashMap<String, Object> jobprops = new HashMap<String, Object>();
43. jobprops.put("PAGE_PATH", action.getPath());
44. // Add the job
45. jobManager.addJob(TOPIC, jobprops);
46. logger.info("=============Topic: '"+TOPIC+"' with payload: '"+action.getPath()
+"' was added to the Job Manager");
47.
48. } catch (Exception e) {
49. logger.error("============= ERROR CREATING JOB : NO PAYLOAD WAS DEFI
NED");
50. e.printStackTrace();
51. }
52. }
53. }
54. }
55. }

Extend and Customize Adobe Experience Manager 259


7. Examine the method handleEvent() and observe the log message, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 260


Task 2: Deploy and test the class

1. In Project Explorer, right-click training.core and choose Run As > Maven install. The build starts.

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. Log in to AEM, navigate to Sites, select TrainingProject Site, and click the thumbnail on English.
The English page is selected.

4. Click Edit (e) in the action bar. The English page opens.

5. Click the Page Information icon at the top left and select Publish Page. The Publish page appears
with All Assets and asset.jpg.

6. Click Publish at the top right and verify if the page is published, as shown:

Extend and Customize Adobe Experience Manager 261


7. On your desktop, navigate to /crx-quickstart/logs/ and open project-trainingproject.log.

8. Scroll down and examine the log message, as shown:

9. Navigate to the AEM Web Console and Select OSGi > Events from the menu. The Events are listed.

ADOBE COPYRIGHT PROTECTED


10. Notice all the events in the event log, as shown:

Extend and Customize Adobe Experience Manager 262


11. Search for training and locate the following event entry in the AEM Web Console, as shown:

d. event.topics=com/day/cq/replication

e. paths=/content/trainingproject/en

f. type=ACTIVATE

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 263


Working with Sling Jobs
Every time there’s the need to process a given event only once, and be sure not to miss it, we can
leverage the Sling Job processing mechanism. We can see it as if we wanted to print a document
with a specific printer on a network: we don’t care if the printer is already busy or even switched
off, any printing job sent to it will be processed once the printer will be ready, and only that chosen
printer will do it.
Contrary to OSGi events, Sling Jobs are guaranteed to be processed, and only once. On the other
hand, they add some overhead, so they would be used only when really necessary (the other
methods discussed in this module could be a better choice in some cases).
Working with Sling Jobs requires the following components:

ADOBE COPYRIGHT PROTECTED


g. A job manager, used to add a job to a queue with data associated to that job

h. A job consumer, to process that job and remove it from the queue

Because of the unicity of the process, once a job consumer has processed the job it gets removed
from the queue and no other consumer would be able to process it (which is a case we should
avoid anyway). Similarly, if no job consumer is registered to process a given job topic, the job stays
in the queue (which is again something that would be considered a deployment error).
Adding a job consists of calling the method addJob() of the org.apache.sling.event.jobs.JobManager
service and passing to it a topic (a string identifying the job) and a HashMap to carry the job
information.
A component that needs to consume a job for processing must implement the interface
org.apache.sling.event.jobs.consumer.JobConsumer and override the process method to get the
Job object org.apache.sling.event.jobs.Job, like seen below:

import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;

@Component (service = JobConsumer.class,


Property = JobConsumer.PROPERTY_TOPICS +
"=com/adobe/training/core/myjobtopic")

public class MyTopicProcessor implements JobConsumer {

@override
public JobResult process(final Job job) {

// do something

Extend and Customize Adobe Experience Manager 264


By convention, topic name is built by using the bundle symbolic name for the prefix (with slashes
instead of dots) and suffixed with a custom label identifying the topic.

Monitoring
To view all registered topics with their statistics, go to the console under Sling > Jobs in the Web
console:
http://localhost:4502/system/console/slingevent

NOTE: This console only shows topics that have been processed by a Job consumer, therefore if a
job is added and never consumed, the corresponding topic won’t be visible. For debugging
purposes, custom jobs can be found in the repository under /var/eventing/jobs.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 265


Exercise 2: Create a job consumer for a topic
In the last exercise, you added a Sling Job with the JobManager. In this exercise, you will process the
Sling Job that was put in the Job Queue.

1. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


2. Copy the file ReplicationLogger.java from /Exercise_Files/11_Event_Handling/.

3. In Eclipse, right-click com.adobe.training.core and paste the file. The ReplicationLogger.java class is
created, as shown:

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/11_Event_Handling/.


Copy and paste the file from the exercise files referenced to ReplicationLogger.java to Eclipse. Please
do not copy the code from this exercise book. The code in the student guide is for illustrative purposes
only.

Extend and Customize Adobe Experience Manager 266


4. Double-click ReplicationLogger.java. The file opens in editor, as shown:

1. package com.adobe.training.core;
2.
3. import org.osgi.service.component.annotations.Component;
4. import org.osgi.service.component.annotations.Reference;
5. import org.apache.sling.api.resource.LoginException;
6. import org.apache.sling.api.resource.ResourceResolver;
7. import org.apache.sling.api.resource.ResourceResolverFactory;
8. import org.apache.sling.event.jobs.Job;
9. import org.apache.sling.event.jobs.consumer.JobConsumer;
10.
11. import org.slf4j.Logger;
12. import org.slf4j.LoggerFactory;
13. import com.day.cq.wcm.api.Page;

ADOBE COPYRIGHT PROTECTED


14. import com.day.cq.wcm.api.PageManager;
15.
16. import java.util.HashMap;
17. import java.util.Map;
18.
19. @Component(service = JobConsumer.class,
20. property = JobConsumer.PROPERTY_TOPICS + "=com/adobe/training/core/replicationjob")
21.
22. public class ReplicationLogger implements JobConsumer {
23. private final Logger logger = LoggerFactory.getLogger(getClass());
24.
25. @Reference
26. private ResourceResolverFactory resourceResolverFactory;
27.
28. @Override
29. public JobResult process(final Job job) {
30.
31. final String pagePath = job.getProperty("PAGE_PATH").toString();
32.
33. Map<String, Object> serviceParams = new HashMap<>();
34. serviceParams.put(ResourceResolverFactory.SUBSERVICE, "training");
35.
36. try (ResourceResolver resourceResolver = resourceResolverFactory
37. .getServiceResourceResolver(serviceParams)) {
38. // Create a Page object to log its title
39. final PageManager pm = resourceResolver.adaptTo(PageManager.class);
40.
41. if (pagePath != null) {
42. final Page page = pm.getContainingPage(pagePath);
43. logger.info("******************* ACTIVATION OF PAGE : {}", page.getTitle());
44. }
45. resourceResolver.close();
46. } catch (LoginException e) {
47. logger.error("Exception with ResourceResolver: ", e);
48. e.printStackTrace();
49. return JobConsumer.JobResult.FAILED;
50. }
51. return JobConsumer.JobResult.OK;
52. } }

Extend and Customize Adobe Experience Manager 267


5. Right-click training.core and select Run As > Maven install. The project is built.

6. Verify that the component is available in AEM at http://localhost:4502/system/console/components and


search for the component by its name, as shown:

ADOBE COPYRIGHT PROTECTED


Notice the ReplicationAction.EVENT_TOPIC property, which is being listened for, and
the @Component(immediate = true) statement, which is needed to ensure the
component com.adobe.training.core.ReplicationLogger is active immediately.

7. To publish (activate) a page, select the page in AEM, and select Quick Publish. The page is published.

8. Navigate to /crx-quickstart/logs/ and open project-trainingproject.log.

9. You should see logged messages giving the titles of pages as they are activated, as shown:

Extend and Customize Adobe Experience Manager 268


10. Go to http://localhost:4502/system/console/slingevent. The events are displayed.

11. Search for replicationjob using browser search. The Topic Statistics for replicationjob is returned.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 269


Sling Scheduler
Sling offers the ability to start jobs automatically at specified times or periodically, by implementing
the standard Java Runnable interface traditionally used to create pooled threads. The Sling
Scheduler mechanism is based on the open-source Quartz library, that allows to define the
schedule with a cron expression.
A cron expression is defined with that pattern:
<Seconds> <Minutes> <hours> <Day of Month> <Month> <Day of Week> <Year>

For example, the expression “0 0 12 * * ?” means “12 pm every day”.

ADOBE COPYRIGHT PROTECTED


The cron expression is set with the property scheduler.expression. Should concurrent execution of
jobs be avoided, the property scheduler.concurrent would be set to false (by default, jobs can be
concurrently executed).
The example below shows a component that registers a scheduled task that will be executed with
the run() method:

@Component (immediate = true,


service = Runnable.class)

public class MyScheduledTask implements Runnable {

@override
public void run() {

// do something

Extend and Customize Adobe Experience Manager 270


Exercise 3: Clean up nodes with a Sling scheduler
In this exercise, you will write a job that periodically deletes the temporary nodes in the repository.
The configuration will be stored in a configuration node, which you can see and edit in the AEM
Web Console.

1. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


2. Copy the file CleanupScheduledTask.java from /Exercise_Files/11_Event_Handling/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/11_Event_Handling/.


Copy and paste the file from the exercise files referenced to CleanupScheduledTask.java to Eclipse.
Please do not copy the code from this exercise book. The code in the student guide is for illustrative
purposes only.

3. Right-click com.adobe.training.core.schedulers and paste the file. The CleanupScheduledTask.java class


is created, as shown:

Extend and Customize Adobe Experience Manager 271


4. Double-click CleanupScheduledTask.java. The file opens in editor, as shown:

1. package com.adobe.training.core.schedulers;
2.
3. import org.apache.sling.api.resource.*;
4. import org.osgi.service.component.annotations.Component;
5. import org.osgi.service.component.annotations.Activate;
6. import org.osgi.service.component.annotations.Modified;
7. import org.osgi.service.component.annotations.Reference;
8. import org.osgi.service.metatype.annotations.AttributeDefinition;
9. import org.osgi.service.metatype.annotations.AttributeType;
10. import org.osgi.service.metatype.annotations.Designate;
11. import org.osgi.service.metatype.annotations.ObjectClassDefinition;
12.
13. import org.slf4j.Logger;

ADOBE COPYRIGHT PROTECTED


14. import org.slf4j.LoggerFactory;
15.
16. import java.util.HashMap;
17. import java.util.Map;
18.
19. @Component(service = Runnable.class, immediate = true)
20.
21. @Designate(ocd = CleanupScheduledTask.Configuration.class, factory=true)
22. public class CleanupScheduledTask implements Runnable {
23. private final Logger logger = LoggerFactory.getLogger(getClass());
24.
25. @Reference
26. private ResourceResolverFactory resolverFactory;
27.
28. private String cleanupPath;
29.
30. @Activate
31. protected void activate(Configuration config) {
32. cleanupPath = config.cleanupPath();
33. }
34.
35. @Modified
36. protected void modified(Configuration config) {
37. logger.info("Configuration modified");
38. }
39.
40. @ObjectClassDefinition(name = "Training Cleanup Service")
41. public @interface Configuration {
42. @AttributeDefinition(
43. name = "Cleanup path",
44. description = "Path to the node which the scheduler removes at a configured interval",
45. type = AttributeType.STRING
46. )
47. String cleanupPath() default "/mypathtraining";
48.
49. @AttributeDefinition(
50. name = "Cron like expression",
51. description = "Run every so often as defined in the cron job expression.",
52. type = AttributeType.STRING
53. )

Extend and Customize Adobe Experience Manager 272


54. String scheduler_expression() default "0/30 * * * * ?";
55. }
56.
57. @Override
58. public void run() {
59.
60. //allows us to get the resourceResolver based on the current session
61. Map<String, Object> params = new HashMap<>();
62. params.put(ResourceResolverFactory.SUBSERVICE, "training");
63.
64. logger.info("!!!Cleanup Task checking: {}", cleanupPath);
65.
66. try (ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(params)){
67.
68. Resource resource = resourceResolver.getResource(cleanupPath);
69.

ADOBE COPYRIGHT PROTECTED


70. if (resource != null) {
71. resourceResolver.delete(resource);
72. resourceResolver.commit();
73. logger.info("!!!node deleted");
74. }
75. } catch (LoginException e) {
76. logger.error("!!!resource does not exist", e);
77. } catch (PersistenceException e) {
78. e.printStackTrace();
79. }
80. }
81. }

5. Right-click training.core, and select Run As > Maven install. The project is built.

6. Go to http://localhost:4512/system/console/configMgr and do a find for "Training Cleanup Service". Click


the + icon on the right side.

Notice the defaults come from the java class we just created.

7. Click Save to enable these path configurations.

8. On your desktop, navigate to /crx-quickstart/logs/ and open project-trainingproject.log.

Extend and Customize Adobe Experience Manager 273


9. Inspect the log output (project-trainingproject.log), as shown:

NOTE: Now you know the scheduler is running. However, you need to test to see if it will delete, or
cleanup /mypathtraining nodes.

10. In CRXDE Lite, right-click the root node (/), and select Create > Create Node named mypathtraining, as
shown:

ADOBE COPYRIGHT PROTECTED


11. Click OK. The node is created.

12. Click Save All to save the changes and wait 30 seconds.

13. Refresh CRXDE Lite. The node gets deleted.

Extend and Customize Adobe Experience Manager 274


14. Verify it is deleted by the cleanup service in the custom log file project-trainingproject.log, as shown:

ADOBE COPYRIGHT PROTECTED


15. In the AEM Web Console, go to OSGi > Configuration (http://localhost:4502/system/console/configMgr)
and search for: Training Cleanup Service, as shown:

Notice the configuration has a + icon next to it. This means that it is a configuration factory, and you
can have multiple instances of this service.

16. Click on Training Cleanup Service to view the details, as shown:

NOTE: When you open the config, you will notice the PID is not set, but rather a Factory PID is
provided. This means that the actual PID is assigned when a configuration is made for this factory.

17. In CRXDE Lite, navigate to /apps/trainingproject/config.author.

Extend and Customize Adobe Experience Manager 275


18. Right-click config.author and select Create > Create Node, and enter the following values, as shown:

• Name: com.adobe.training.core.schedulers.CleanupScheduledTask-mycustompath
• Select sling:OsgiConfig from Type drop-down menu.

ADOBE COPYRIGHT PROTECTED


NOTE: mycustompath was added as a unique identifier (it can be anything) to the PID. This is needed
to make an instance of the config factory.

19. To the new node, add these properties, as shown:

• Name: cleanupPath
Type: (String)
Value: /mycustompath
• Name: scheduler.expression
Type: (String)
Value: 0/30 * * * * ?

Extend and Customize Adobe Experience Manager 276


20. Click Save All to save the changes.

21. To test your configuration, right-click the root node (/), as shown and select Create > Create Node.

ADOBE COPYRIGHT PROTECTED


22. Enter the following values for the node:

• Name: mycustompath

• Type: nt:unstructured

23. Click Save All. The mycustompath node is created.

24. Wait 30 seconds and refresh CRXDE Lite. Observe the node is deleted.

25. Observe the node being deleted in the logs ~/crx-quickstart/logs/project-trainingproject.log, as


shown:

Extend and Customize Adobe Experience Manager 277


Extra Credit Exercise: Add more Cleanup paths
1. To add more cleanup paths, copy and paste the OSGi config node and rename the unique
identifier, for example:

a. /apps/trainingproject/config.author/com.adobe.training.core.schedulers.CleanupScheduledTask-
mysecondpath

b. Update the cleanupPath property:

cleanupPath (String) /mysecondpath

ADOBE COPYRIGHT PROTECTED


2. Save the changes.

3. Observe how both the paths are not being cleaned up.

Extend and Customize Adobe Experience Manager 278


Sling Resource Change Listening
For a component to be notified of changes happening in the repository, some techniques with fewer
overhead than Sling Jobs and a richer interface than OSGi events can be considered. For example, the JCR
Observation, which is a request in the JCR specification, is typically a mechanism that allows to monitor
changes on nodes or properties in a JCR repository, at a certain path and under certain conditions on
nodetypes or UUIDs. This can be achieved by implementing the javax.jcr.observation.EventListener
interface and creating an event listener with the javax.jcr.observation.ObservationManager interface.
However, because it’s usually a better practice to use higher level APIs, we can conveniently do the same
with Sling and the org.apache.sling.api.resource.observation.ResourceChangeListener interface.

Introduced with Sling 9 (2017), its goal is to provide an observation mechanism, similar to the JCR

ADOBE COPYRIGHT PROTECTED


observation, but on Sling resources. Prior to this API, resource changes were only propagated through OSGi
Even Admin, with all its disadvantages.
In the same manner that an observation is initiated with the JCR observation API, the
ResourceChangeListener is registered with a mandatory property specifying the paths under which the
change of the resource (or resource provider) is to be monitored, and optionally with the type of changes
expected.

Then resource changes can be caught by overriding the method onChange() as seen in this example:

import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;

@Component (immediate = true,


service = ResourceChangeListener.class
property = {"resource.paths=/content/mypath ",
"resource.change.types=CHANGED"})

public class MyResourceListener implements ResourceChangeListener {


@override
public void onChange(List<ResourceChange> changes) {

// do something

Extend and Customize Adobe Experience Manager 279


Reasons to prefer this method
The advantage of this method over OSGi Events or Sling Jobs is that it allows to restrict events to
one (or several) specified path(s). Another advantage is that bulk listening operations will result in
a single thread (the listener will only be executed once), whereas with OSGi Events or Sling Jobs
the handler would be called as many times as the amount of operations.

For these reasons, listening to only resource-related events will be preferably done using the
ResourceChangeListener, for performance considerations.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 280


Exercise 4: Create a resource listener
In this exercise, you will create a resource listener that listens for resource changes made to the jcr:title
property of a page, and displays the notification accordingly.

1. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


2. Copy the file TitlePropertyListener.java from /Exercise_Files/11_Event_Handling.

3. Right-click com.adobe.training.core.listeners and paste the file. The TitlePropertyListener.java class is


created, as shown:

4. Double-click TitlePropertyListener.java. The file opens in editor, as shown:

1. package com.adobe.training.core.listeners;
2.
3. import org.apache.sling.api.resource.*;
4. import org.osgi.service.component.annotations.Component;
5. import org.osgi.service.component.annotations.Reference;
6.
7. import org.apache.sling.api.resource.observation.ResourceChange;
8. import org.apache.sling.api.resource.observation.ResourceChangeListener;
9.
10. import java.util.HashMap;

Extend and Customize Adobe Experience Manager 281


11. import java.util.List;
12. import java.util.Map;
13.
14. import org.slf4j.Logger;
15. import org.slf4j.LoggerFactory;
16.
17. @Component( immediate = true,
18. name = "Title Property Listener",
19. property = {"resource.paths=/content/trainingproject/fr",
20. "resource.change.types=CHANGED"} )
21.
22. public class TitlePropertyListener implements ResourceChangeListener{
23.

ADOBE COPYRIGHT PROTECTED


24. private final Logger logger = LoggerFactory.getLogger(getClass());
25.
26. @Reference
27. private ResourceResolverFactory resourceResolverFactory;
28.
29. public void onChange(List<ResourceChange> changes) {
30.
31. for (final ResourceChange change : changes) {
32.
33. logger.info("Change type: {}", change);
34.
35. Map<String,Object> serviceParams = new HashMap<String,Object>(){{put(ResourceRes
olverFactory.SUBSERVICE, "training");}};
36. try (ResourceResolver rr = resourceResolverFactory.getServiceResourceResolver(service
Params))
37. {
38. logger.error("Repository login success");
39.
40. String JCR_TITLE_PROP = "jcr:title";
41.
42. Resource changedRes = rr.getResource(change.getPath());
43.
44. String titleProperty = (changedRes != null) ? changedRes.getValueMap().get(JCR_TITL
E_PROP, String.class) : "";
45.
46. if (titleProperty != null && !titleProperty.isEmpty() && !titleProperty.endsWith("!")) {
47. ModifiableValueMap modChangedResource = changedRes.adaptTo(ModifiableVal
ueMap.class);
48. modChangedResource.put(JCR_TITLE_PROP, titleProperty + "!");
49. rr.commit();
50. logger.info("*************Property updated: {}", change.getPath());

Extend and Customize Adobe Experience Manager 282


51. }
52.
53. } catch(PersistenceException e ){
54. logger.error("Failed to write the resource change.");
55. } catch(LoginException e ){
56. logger.error("Repository login failed.");
57. }
58. }
59. }
60. }

ADOBE COPYRIGHT PROTECTED


5. Right-click training.core and select Run As > Maven install. The project is built.

6. Verify that the component is available in AEM at: http://localhost:4502/system/console/components


,as shown:

Extend and Customize Adobe Experience Manager 283


7. In CRXDE Lite, navigate to /apps/content/trainingproject/fr/jcr:content, as shown:

ADOBE COPYRIGHT PROTECTED


8. Change the value of the property jcr:title to Test, as shown:

9. Click Save All. The changes are saved.

Extend and Customize Adobe Experience Manager 284


10. Refresh CRXDE Lite to see if ! is added by com.adobe.training.core.TitlePropertyListener component
with the value Test, as shown:

ADOBE COPYRIGHT PROTECTED


11. Go to the French page in our TrainingProject Site,
http://localhost:4502/editor.html/content/trainingproject/fr.html.

12. Click to the Page information icon > Open Properties. The Page Properties window (with the name
Test!) opens.

13. Change the Title, as shown:

Extend and Customize Adobe Experience Manager 285


14. Click Save & Close. The page is reloaded, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 286


References
For more information on the events in the AEM Web Console:
http://localhost:4502/system/console/events

http://localhost:4502/system/console/slingevent

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 287


Module 12: Deep Dive into AEM APIs

Introduction

APIs are at the highest layer of the Adobe Experience Manager (AEM) architecture stack, and are most
closely related to various business tasks a typical content producer would create in AEM. You can use APIs
in AEM to develop a custom data importer service that helps import data from a site.

ADOBE COPYRIGHT PROTECTED


To address some business requirements, you may need to import external data into your AEM site. For
example, you can import data from an external social media site. You can use AEM APIs to help build
workflows with specific business needs.

Objectives

• Describe polling importers

• Create polling importers

• Programmatically create an AEM page

• Create AEM pages based on a CSV file input

• Describe AEM projects

• Create a project and project templates

• Execute the workflow

• Build and test a workflow

• Customize the process step in workflow

Extend and Customize Adobe Experience Manager 288


Polling Importers
The polling importer is a framework to repeatedly import content from external sources into your
repository. The idea behind a feed importer is to poll a remote resource at a specified interval, parse it, and
create nodes in the content repository that represents the content of the remote resource.

The polling importer is used to support:

• The auto-blogging feature, which automatically creates blog posts from an external RSS or Atom
feed

• iCalendar subscriptions, which automatically creates calendar events from an external ICS file or
subscribes to or from other calendars

ADOBE COPYRIGHT PROTECTED


The polling importer is found in the WCM Administrative interface under Tools > Importers > Feed
Importer. You can double-click the Feed Importer node to configure standard polling importers for RSS,
Atom, Calendar, IMAP, and POP3.

To implement customized polling importer, use the interface, com.day.cq.pollingimporter.Importer and


develop an OSGi bundle.

Extend and Customize Adobe Experience Manager 289


Exercise 1: Creating a polling importer
In this exercise, you will create a polling importer to import stock information to JCR.
This exercise includes two tasks:
1. Create a polling importer
2. Deploy and test the polling importer
Task 1: Creating a polling importer

1. Double-click the Eclipse shortcut on the desktop. The Eclipse Launcher opens.

ADOBE COPYRIGHT PROTECTED


2. Specify the workspace as C:\Workspace, and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

4. Copy the file StockDataImporter.java from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


12_Deep_Dive_into_AEM_APIs /. Copy and paste the file from the exercise files referenced to
StockDataImporter.java to Eclipse. Please do not copy the code from this student guide. The
code in the student guide is for illustrative purposes only.

Extend and Customize Adobe Experience Manager 290


5. In Eclipse, right-click com.adobe.training.core and paste the file. The StockDataImporter.java class is
created, as shown:

6. Double-click StockDataImporter.java. The file opens in editor, as shown:

ADOBE COPYRIGHT PROTECTED


56. package com.adobe.training.core;
57.
58. import java.io.IOException;
59. import java.io.InputStream;
60. import java.io.InputStreamReader;
61. import java.net.MalformedURLException;
62. import java.net.URL;
63. import java.time.Instant;
64. import java.time.LocalDateTime;
65. import java.time.ZoneId;
66. import java.time.ZonedDateTime;
67. import java.time.format.DateTimeFormatter;
68.
69. import javax.jcr.Node;
70. import javax.jcr.RepositoryException;
71. import javax.jcr.Session;
72. import javax.net.ssl.HttpsURLConnection;
73.
74. import org.apache.sling.api.resource.Resource;
75. import org.apache.sling.jcr.api.SlingRepository;
76. import org.osgi.service.component.annotations.Component;
77. import org.osgi.service.component.annotations.Reference;
78. import org.slf4j.Logger;
79. import org.slf4j.LoggerFactory;
80.
81. import com.day.cq.commons.jcr.JcrUtil;
82. import com.day.cq.polling.importer.ImportException;
83. import com.day.cq.polling.importer.Importer;
84. import com.google.gson.JsonElement;
85. import com.google.gson.JsonObject;
86. import com.google.gson.JsonParser;
87.
88. /**
89. * This class imports from IEX api and creates the data structure example below:
90. *
91. * /content/stocks/
92. * + <STOCK_SYMBOL> [sling:OrderedFolder]
93. * + lastTrade [nt:unstructured]

Extend and Customize Adobe Experience Manager 291


94. * - companyName = <value>
95. * - sector = <value>
96. * - lastTrade = <value>
97. * - timeOfUpdate = <value>
98. * - dayOfLastUpdate = <value>
99. * - openPrice = <value>
100. * - rangeHigh = <value>
101. * - rangeLow = <value>
102. * - volume = <value>
103. * - upDownPrice = <value>
104. * - week52High = <value>
105. * - week52Low = <value>
106. * - ytdChange = <value>
107. *
108. */
109.

ADOBE COPYRIGHT PROTECTED


110. @Component(service = Importer.class, property = "importer.scheme=stock")
111.
112. public class StockDataImporter implements Importer {
113. private final Logger logger = LoggerFactory.getLogger(getClass());
114.
115. //controls the property NAME values to be written to the JCR in lines 170-180:
116. private static final String COMPANY = "companyName";
117. private static final String SECTOR = "sector";
118. private static final String LASTTRADE = "lastTrade";
119. private static final String UPDATETIME = "timeOfUpdate";
120. private static final String DAYOFUPDATE = "dayOfLastUpdate";
121. private static final String OPENPRICE = "openPrice";
122. private static final String RANGEHIGH = "rangeHigh";
123. private static final String RANGELOW = "rangeLow";
124. private static final String VOLUME = "volume";
125. private static final String UPDOWN = "upDown";
126. private static final String WEEK52HIGH = "week52High";
127. private static final String WEEK52LOW = "week52Low";
128. private static final String YTDCHANGE = "ytdPercentageChange";
129.
130. @Reference
131. private SlingRepository repo;
132.
133. @Override
134. public void importData(final String scheme, final String dataSource, final Resource resource)
135. throws ImportException {
136. try {
137. // dataSource will be interpreted as the stock symbol
138.
139. //BW - Updated using the IEX api - https://iextrading.com/developer/docs/#quote
140. String iexApiUrl = "https://api.iextrading.com/1.0/stock/" + dataSource + "/quote";
141. URL sourceUrl = new URL(iexApiUrl);
142. HttpsURLConnection request = (HttpsURLConnection) sourceUrl.openConnection();
143. request.connect();
144.
145. // Convert IEX data return to a JSON object
146. JsonParser parser = new JsonParser();
147. JsonElement root = parser.parse(new InputStreamReader((InputStream) request.getContent()));
148. //JsonObject will give access to quote data via keys
149. JsonObject allQuoteData = root.getAsJsonObject();

Extend and Customize Adobe Experience Manager 292


150.
151. logger.info("Last trade for stock symbol {} was {}", dataSource, allQuoteData.get("latestPrice"));
152.
153. writeToRepository(dataSource, allQuoteData, resource);
154.
155. }
156. catch (MalformedURLException e) {
157. logger.error("MalformedURLException", e);
158. }
159. catch (IOException e) {
160. logger.error("IOException", e);
161. }
162. catch (RepositoryException e) {
163. logger.error("RepositoryException", e);
164. }
165.

ADOBE COPYRIGHT PROTECTED


166. }
167.
168. /**
169. * Creates the stock data structure
170. *
171. * + <STOCK_SYMBOL> [sling:OrderedFolder]
172. * + lastTrade [nt:unstructured]
173. * - companyName = <value>
174. * - sector = <value>
175. * - lastTrade = <value>
176. * - timeOfUpdate = <value>
177. * - dayOfLastUpdate = <value>
178. * - openPrice = <value>
179. * - rangeHigh = <value>
180. * - rangeLow = <value>
181. * - volume = <value>
182. * - upDownPrice = <value>
183. * - week52High = <value>
184. * - week52Low = <value>
185. * - ytdChange = <value>
186. */
187. private void writeToRepository(final String stockSymbol, final JsonObject quoteData, final Resource resource) thr
ows RepositoryException {
188.
189. logger.info("Stock Symbol: " + stockSymbol);
190. logger.info("JsonObject to Write: " + quoteData.toString());
191.
192. Session session= repo.loginService("training",null);
193.
194. //NOTE: /content/stocks has to exist in the JCR to successfully adapt resource to Node here.
195. Node parent = resource.adaptTo(Node.class);
196.
197. if(parent == null) {
198. logger.warn("The node " + resource.getPath() + " does not exist. Autocreating.");
199. parent = JcrUtil.createPath(resource.getPath(), "sling:OrderedFolder", session);
200. }
201.
202. if (parent != null) {
203. Node stockPageNode = JcrUtil.createPath(parent.getPath() + "/" + stockSymbol, "sling:OrderedFolder", session
);

Extend and Customize Adobe Experience Manager 293


204. Node lastTradeNode = JcrUtil.createPath(stockPageNode.getPath() + "/lastTrade", "nt:unstructured", session);
205. //use java.time api for Date & Time. Set time to EST for our application
206. ZoneId timeZone = ZoneId.of("America/New_York");
207. long latestUpdateTime = quoteData.get("latestUpdate").getAsLong();
208. LocalDateTime timePerLatestUpdate = LocalDateTime.ofInstant(Instant.ofEpochMilli(latestUpdateTime),
209. timeZone);
210. ZonedDateTime timeWithZone = ZonedDateTime.of(timePerLatestUpdate, timeZone);
211. DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a zz");
212. //will store timeOfUpdate as: Hour:Minute AM/PM, TimeZone e.g. 11:34 AM, EDT
213. String iexUpdateTimeOfDay = timeWithZone.format(timeFormatter);
214. DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("E MMMM d, yyyy");
215. String dayOfUpdate = timeWithZone.format(dayFormatter);
216. lastTradeNode.setProperty(COMPANY, quoteData.get("companyName").getAsString());
217. lastTradeNode.setProperty(SECTOR, quoteData.get("sector").getAsString());
218. /*
219. * latestPrice & latestUpdate from IEX both STOP at 4pm Eastern Time (New York)

ADOBE COPYRIGHT PROTECTED


220. * so - effectively, updates stop at this point and do not resume until the next trading day
221. */
222. lastTradeNode.setProperty(LASTTRADE, quoteData.get("latestPrice").getAsDouble());
223. lastTradeNode.setProperty(UPDATETIME, iexUpdateTimeOfDay);
224. lastTradeNode.setProperty(DAYOFUPDATE, dayOfUpdate);
225. lastTradeNode.setProperty(OPENPRICE, quoteData.get("open").getAsDouble());
226. lastTradeNode.setProperty(RANGEHIGH, quoteData.get("high").getAsDouble());
227. lastTradeNode.setProperty(RANGELOW, quoteData.get("low").getAsDouble());
228. lastTradeNode.setProperty(VOLUME, quoteData.get("latestVolume").getAsLong());
229. lastTradeNode.setProperty(UPDOWN, quoteData.get("change").getAsDouble());
230. lastTradeNode.setProperty(WEEK52HIGH, quoteData.get("week52High").getAsDouble());
231. lastTradeNode.setProperty(WEEK52LOW, quoteData.get("week52Low").getAsDouble());
232. lastTradeNode.setProperty(YTDCHANGE, quoteData.get("ytdChange").getAsDouble());
233. } else {
234. logger.error("###### JCR resource: " + resource.getPath() + " needs to be created for Importer to write data");
235. }
236. session.save();
237. session.logout();
238. }
239.
240. @Override
241. public void importData(String scheme, String dataSource, Resource target, String login, String password) throws I
mportException {
242. importData(scheme, dataSource, target);
243.
244. }
245. }
246.

Extend and Customize Adobe Experience Manager 294


Task 2: Deploy and test the polling importer

1. In Project Explorer, right-click training.core and choose Run As > Maven install. The build starts.

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. Open CRXDE Lite http://localhost:4502/crx/de/index.jsp and navigate to etc > importers > polling,
as shown:

Extend and Customize Adobe Experience Manager 295


4. Right-click polling, and select Create > Create node. Provide the following values:

a. Select sling:folder from the Type drop-down menu.

b. Enter adobe_stock for the Name field.

ADOBE COPYRIGHT PROTECTED


5. Click OK. The node adobe_stock is created.

6. Click Save All, as shown:

Extend and Customize Adobe Experience Manager 296


7. On the adobe_stock folder node, add the following properties, as shown:

Name: interval
Type: String
Value: 300

Name: source
Type: String
Value: stock:ADBE

Name: target
Type: String

ADOBE COPYRIGHT PROTECTED


Value: /content/stocks

8. Click Mixins in the toolbar at the top, click +, and select cq:PollConfig from the drop-down menu, as
shown:

This helps JCR identify a Mixins configuration.

Extend and Customize Adobe Experience Manager 297


9. Click OK. The Mixins configuration is created.

10. Click Save All to save the properties created on the adobe_stock node.

NOTE: The configurations you created will provide the information to the custom polling importer
you created, StockDataImporter.java class. As a result, in 300 seconds (5 minutes), you should see a
new node created at the target path (/content) provided in the node.

11. Refresh /content in CRXDE Lite, and notice that there is a ADBE node created.

ADOBE COPYRIGHT PROTECTED


Notice that there is a lastTrade node under the ADBE node, and the properties on the lastTrade
node is the data you imported. This data is shown in the screenshot below:

Extend and Customize Adobe Experience Manager 298


Creating AEM Pages and Assets Programmatically
Following are the best practices for data migration are:
• Create pages dynamically

• Create assets dynamically

• Identify cost benefit

Create Pages Dynamically

AEM has high-level APIs that you can use to create pages based on the underlying data structures.

ADOBE COPYRIGHT PROTECTED


You can create paragraph or text nodes, tag a page, or activate a page.
A page consists of nodes and properties along with a sling:resourceType. You can use the
com.day.cq.wcm.api.PageManager class to point to the underlying resources in xml and automate
the process of data migration.

Create Assets Dynamically

Assets consist of nodes and properties along with a sling:resourceType. You can use the
com.day.cq.dam.api.AssetManager to create the asset, and the com.day.cq.tagging.TagManager
APIs to tag the assets. Using these APIs, you can create assets, tag them, as well as add metadata to
the assets.
You can use this process for migration by pointing to the existing assets and creating the asset in a
Digital Asset Manager, with all the asset information that is required.

Identify Cost Benefit

Based on the complexity and size of the migrating system, you can use any of the following three
methods for migration:
• Manual migration (human entry): Hire emplyees to enter the data into a format that is compatible
with AEM.

• Automated migration (using APIs): Export data as XML from the legacy system, write a script to
convert the data into a JCR-friendly XML, and import that package into JCR.

Hybrid migration (a combination of manual and automated): Import the bulk of the data through
XML, and hire emplyees to update the data manually. For example, you can follow this method to
add metadata to modify the data.

Extend and Customize Adobe Experience Manager 299


Exercise 2: Programmatically create an AEM page
In this exercise, you will create a page programmatically, with a servlet and a resource from JCR.
This exercise includes two tasks:
1. Create a page creator
2. Deploy and test the page creator
Task 1: Create an AEM project

1. Double-click the Eclipse shortcut on the desktop. The Eclipse Launcher opens.

ADOBE COPYRIGHT PROTECTED


2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

Extend and Customize Adobe Experience Manager 300


4. Copy the file PageCreator.java from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/.

NOTE: The code is provided as part of Exercise_Files under /Exercise_Files/


12_Deep_Dive_into_AEM_APIs/. Copy and paste the file from the exercise files referenced to
PageCreator.java to Eclipse. Please do not copy the code from this student guide. The code in the student
guide is for illustrative purposes only.

5. In Eclipse, right-click com.adobe.training.core.servlets, and paste the file. The PageCreator.java class is
created, as shown:

ADOBE COPYRIGHT PROTECTED


6. Double-click PageCreator.java. The file opens in editor, as shown:

1. package com.adobe.training.core.servlets;
2.
3. import java.io.IOException;
4. import java.util.HashMap;
5.
6. import javax.jcr.Session;
7. import javax.servlet.Servlet;
8. import javax.servlet.ServletException;
9.
10. import com.google.gson.Gson;
11. import org.osgi.service.component.annotations.Component;
12. import org.osgi.service.component.annotations.Reference;
13.
14. import org.apache.sling.api.SlingHttpServletRequest;
15. import org.apache.sling.api.SlingHttpServletResponse;
16. import org.apache.sling.api.resource.Resource;
17. import org.apache.sling.api.servlets.SlingAllMethodsServlet;
18.
19. import org.slf4j.Logger;
20. import org.slf4j.LoggerFactory;
21.
22. import com.day.cq.replication.ReplicationActionType;
23. import com.day.cq.replication.Replicator;
24. import com.day.cq.tagging.Tag;
25. import com.day.cq.tagging.TagManager;
26. import com.day.cq.wcm.api.Page;
27. import com.day.cq.wcm.api.PageManager;
28.
29. /**

Extend and Customize Adobe Experience Manager 301


30. * This Servlet ingests a comma delimited string in the form of:
31. *
32. * New Page Path, Page Title, Page Tag, Auto Publish, Template
33. *
34. * And outputs the AEM page created.
35. *
36. * To test, you must create a content node with a resourceType=trainingproject/tools/pagecreator
37. *
38. * /content/pagecreator {sling:resourceType=trainingproject/tools/pagecreator}
39. *
40. * Example cURL Command:
41. * $ curl -u admin:admin -X POST http://localhost:4512/content/pagecreator.json -
F importer="/content/trainingproject/en/community,Our Community,/apps/trainingproject/templates/page-
home,/etc/tags/we-retail/activity/biking,TRUE"
42. *
43. */

ADOBE COPYRIGHT PROTECTED


44.
45.
46. @Component(service = Servlet.class,
47. property = {"sling.servlet.resourceTypes=trainingproject/tools/pagecreator",
48. "sling.servlet.methods=POST"
49. })
50.
51. public class PageCreator extends SlingAllMethodsServlet{
52. private final Logger logger = LoggerFactory.getLogger(this.getClass());
53.
54. private static final long serialVersionUID = 1L;
55.
56. @Reference
57. private Replicator replicator;
58.
59. private Resource resource;
60.
61. @Override
62. public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)throws ServletException,IOEx
ception{
63. response.setHeader("Content-Type", "application/json");
64. HashMap<String, String> resultObject = new HashMap<String, String>();
65. Gson jsonResponse = new Gson();
66.
67. resource = request.getResource();
68.
69. String param = request.getParameter("importer");
70. String[] newPage = param.split(",");
71. try {
72. resultObject = createTrainingPage(newPage[0], newPage[1], newPage[2], newPage[3], newPage[4]);
73. } catch (Exception e) {
74. resultObject.put("Status", "Could not properly parse");
75. logger.error("Failure to create page: " + e);
76. }
77.
78. if(resultObject != null){
79. //Write the result to the page
80. response.getWriter().print(jsonResponse.toJson(resultObject));
81. response.getWriter().close();
82. }

Extend and Customize Adobe Experience Manager 302


83. }
84.
85. /** Helper method to create the page based on available input
86. *
87. * @param path JCR location of the page to be created
88. * @param title Page Title
89. * @param template AEM Template this page should be created from. The template must exist in the JCR already.
90. * @param tag Tag must already be created in AEM. The tag will be in the form of a path. Ex /etc/tags/marketing/intere
st
91. * @param publish boolean to publish the page
92. * @return HashMap
93. */
94. private HashMap<String, String> createTrainingPage(String path, String title, String template, String tag, String publish)
throws Exception {
95. HashMap<String, String> pageInfo = new HashMap<String, String>();
96.

ADOBE COPYRIGHT PROTECTED


97. if (path != null) {
98. //Parse the path to get the pageNodeName and parentPath
99. int lastSlash = path.lastIndexOf("/");
100. String pageNodeName = path.substring(lastSlash + 1);
101. String parentPath = path.substring(0, lastSlash);
102.
103. //Set a default template if none is given
104. if (template == null || template.isEmpty()) {
105. template = "/apps/trainingproject/templates/page-content";
106. }
107.
108. //Create page
109. PageManager pageManager = resource.getResourceResolver().adaptTo(PageManager.class);
110.
111. if (pageManager != null && !parentPath.isEmpty() && !pageNodeName.isEmpty()) {
112. Page p = pageManager.create(parentPath,
113. pageNodeName,
114. template,
115. title);
116.
117. //Add a tag to the page
118. if (tag != null && !tag.isEmpty()) {
119. //TagManager can be retrieved via adaptTo
120. TagManager tm = resource.getResourceResolver().adaptTo(TagManager.class);
121. if (tm != null) {
122. tm.setTags(p.getContentResource(),
123. new Tag[]{tm.resolve(tag)},
124. true);
125. }
126. }
127.
128.
129. //Publish page if requested
130. boolean publishPage = Boolean.parseBoolean(publish);
131. if (publishPage) {
132. //Replicator is exposed as a service
133. replicator.replicate(resource.getResourceResolver().adaptTo(Session.class),
134. ReplicationActionType.ACTIVATE,
135. p.getPath());
136. }

Extend and Customize Adobe Experience Manager 303


137.
138. pageInfo.put("Status", "Successful");
139. pageInfo.put("Location", p.getPath());
140. pageInfo.put("Title", p.getTitle());
141. pageInfo.put("Template Used", p.getTemplate().getPath());
142. pageInfo.put("Tagged with", p.getTags()[0].getTitle());
143. pageInfo.put("Was Published", String.valueOf(publishPage));
144. }
145. }
146.
147. if(pageInfo.isEmpty()) {
148. pageInfo.put("Status", "Could not create a page");
149. }
150. return pageInfo;
151. } }

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 304


Task 2: Deploy and test the page creator

1. In Project Explorer, right-click training.core and choose Run As > Maven install. The build starts.

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. Open CRXDE Lite http://localhost:4502/crx/de/index.jsp and navigate to /content.

4. Right-click content, and select Create > Create node. Provide the following values:

a. Enter pagecreator for the Name field.

b. Select nt:unstructured from the Type drop-down menu.

5. Click OK. The node pagecreator is created.

6. Click Save All.

Extend and Customize Adobe Experience Manager 305


7. On the pagecreator folder node, add the following property, as shown:

• Name: sling:resourceType
• Type: String
• Value: trainingproject/tools/pagecreator

ADOBE COPYRIGHT PROTECTED


8. Click Save All to save the properties created on the pagecreator node.

NOTE: You have content that you can post and a servlet that will be triggered. You can now test with
a cURL command.

9. Copy the first command from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/pagecreator-curl-


command.txt.

10. Open your command terminal, and run the following command:

NOTE: If you are on a Mac, you can format the returned json by adding | json_pp.

Extend and Customize Adobe Experience Manager 306


11. Verify the newly created page in Sites > TrainingProject Site > English, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: If you want to use the browser rather than cURL, you can install the http-pagecreator.zip
content package (from the exercise folder) and do a request for
http://localhost:4502/content/pagecreator.html.

Extend and Customize Adobe Experience Manager 307


Exercise 3: Create AEM pages programmatically
In this exercise, you will create a website programmatically, with a servlet and a csv file.
This exercise includes two tasks:
1. Create a CSV page creator
2. Deploy and test the CSV page creator

Task 1: Create a CSV page creator

1. Double-click the Eclipse shortcut on the desktop. The Eclipse Launcher opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

ADOBE COPYRIGHT PROTECTED


3. In Project Explorer, navigate to training.core > src/main/java, as shown:

4. Copy the file CSVPageCreator.java from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


12_Deep_Dive_into_AEM_APIs /. Copy and paste the file from the exercise files referenced
to CSVPageCreator.java to Eclipse. Please do not copy the code from this student guide. The
code in the student guide is for illustrative purposes only.

Extend and Customize Adobe Experience Manager 308


5. In Eclipse, right-click com.adobe.training.core.servlets and paste the file. The CSVPageCreator.java class
is created, as shown:

6. Double-click CSVPageCreator.java. The file opens in editor.

ADOBE COPYRIGHT PROTECTED


1. package com.adobe.training.core.servlets;
2.
3. import java.io.BufferedReader;
4. import java.io.ByteArrayInputStream;
5. import java.io.IOException;
6. import java.io.InputStream;
7. import java.io.InputStreamReader;
8. import java.util.HashMap;
9.
10. import javax.jcr.Session;
11. import javax.servlet.Servlet;
12. import javax.servlet.ServletException;
13.
14. import com.google.gson.Gson;
15.
16. import org.osgi.service.component.annotations.Reference;
17. import org.osgi.service.component.annotations.Component;
18.
19. import org.apache.sling.api.SlingHttpServletRequest;
20. import org.apache.sling.api.SlingHttpServletResponse;
21. import org.apache.sling.api.resource.Resource;
22. import org.apache.sling.api.servlets.SlingAllMethodsServlet;
23.
24. import org.slf4j.Logger;
25. import org.slf4j.LoggerFactory;
26.
27. import com.day.cq.replication.ReplicationActionType;
28. import com.day.cq.replication.Replicator;
29. import com.day.cq.tagging.Tag;
30. import com.day.cq.tagging.TagManager;
31. import com.day.cq.wcm.api.Page;
32. import com.day.cq.wcm.api.PageManager;
33.
34. /**
35. * This Servlet ingests a .csv file with the following columns:
36. *
37. * New Page Path, Page Title, Page Tag, Auto Publish, Template
38. *
39. * And outputs AEM pages created.
40. *

Extend and Customize Adobe Experience Manager 309


41. * To test, you must create a content node with a resourceType=trainingproject/tools/pagecreator
42. *
43. * /content/pagecreator {sling:resourceType=trainingproject/tools/pagecreator}
44. *
45. * Example cURL Command:
46. * $ curl -u admin:admin -X POST http://localhost:4512/content/pagecreator.csv.json -F [email protected]
47. *
48. */
49.
50. @Component( service = Servlet.class,
51. property = {
52. "sling.servlet.resourceTypes=trainingproject/tools/pagecreator",
53. "sling.servlet.selectors=csv",
54. "sling.servlet.methods=POST"
55. }
56. )

ADOBE COPYRIGHT PROTECTED


57. public class CSVPageCreator extends SlingAllMethodsServlet {
58.
59. private static final long serialVersionUID = 1L;
60. private final Logger logger = LoggerFactory.getLogger(getClass());
61.
62. @Reference
63. private Replicator replicator;
64.
65. private Resource resource;
66.
67. public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOE
xception {
68. response.setHeader("Content-Type", "application/json");
69. HashMap<String, HashMap<String, String>> resultObject = new HashMap<String, HashMap<String, String>>();
70. Gson jsonResponse = new Gson();
71. resource = request.getResource();
72.
73. String param = request.getParameter("importer");
74. byte[] input = param.getBytes();
75. InputStream stream = new ByteArrayInputStream(input);
76. try {
77. resultObject = readCSV(stream);
78. } catch (Exception e) {
79. logger.error("Failure to Read CSV: " + e);
80. }
81.
82. if (resultObject != null) {
83. //Write the result to the page
84. response.getWriter().print(jsonResponse.toJson(resultObject));
85. response.getWriter().close();
86. }
87. }
88.
89. /**
90. * Reads the CSV file. The CSV file MUST be in the form of:
91. * <p>
92. * JCR path, Page Title, Page Template, AEM Tag, Publish boolean
93. *
94. * @param stream Stream from the CSV
95. * @return JSON object that contains the results of the page creation process

Extend and Customize Adobe Experience Manager 310


96. */
97. private HashMap<String, HashMap<String, String>> readCSV(InputStream stream) throws IOException, Exception {
98. HashMap<String, HashMap<String, String>> out = new HashMap<String, HashMap<String, String>>();
99. BufferedReader br = new BufferedReader(new InputStreamReader(stream));
100.
101. String line;
102. String[] newPage;
103. HashMap<String, String> createdPageObject = null;
104. //Read each line of the CSV
105. while ((line = br.readLine()) != null) {
106. newPage = line.split(",");
107. String aemTag = null;
108. String publishFlag = null;
109. String aemTemplatePath = null;
110.
111. //If the line has a template, tag, publish flag, set those variables

ADOBE COPYRIGHT PROTECTED


112. if (newPage.length == 5) {
113. aemTemplatePath = newPage[2];
114. aemTag = newPage[3];
115. publishFlag = newPage[4];
116. } else if (newPage.length == 4) {
117. aemTemplatePath = newPage[2];
118. aemTag = newPage[3];
119. } else if (newPage.length == 3) {
120. publishFlag = newPage[2];
121. }
122.
123. //As long as there is a path and title, the page can be created
124. if ((newPage.length > 1)
125. && !newPage[0].isEmpty()
126. && !newPage[1].isEmpty()) {
127. String path = newPage[0];
128. String title = newPage[1];
129. try {
130. createdPageObject = createTrainingPage(path, title, aemTemplatePath, aemTag, publishFlag);
131. } catch (Exception e) {
132. logger.error(path + " not created successfully: " + e);
133. }
134.
135. //add the status of the row into the json array
136. out.put(title, createdPageObject);
137. createdPageObject = null;
138. } else {
139. createdPageObject = new HashMap<String, String>();
140. createdPageObject.put("Status", "Could not properly parse");
141. out.put(line,createdPageObject);
142. createdPageObject = null;
143. }
144. }
145. br.close();
146. return out;
147. }
148.
149. /**
150. * Helper method to create the page based on available input
151. *

Extend and Customize Adobe Experience Manager 311


152. * @param path JCR location of the page to be created
153. * @param title Page Title
154. * @param template AEM Template this page should be created from. The template must exist in the JCR already.
155. * @param tag Tag must already be created in AEM. The tag will be in the form of a path. Ex /etc/tags/marketing/int
erest
156. * @param publish boolean to publish the page
157. */
158. private HashMap<String, String> createTrainingPage(String path, String title, String template, String tag, String publish)
throws Exception {
159. HashMap<String, String> pageInfo = new HashMap<String, String>();
160.
161. if (path != null) {
162. //Parse the path to get the pageNodeName and parentPath
163. int lastSlash = path.lastIndexOf("/");
164. String pageNodeName = path.substring(lastSlash + 1);
165. String parentPath = path.substring(0, lastSlash);

ADOBE COPYRIGHT PROTECTED


166.
167. //Set a default template if none is given
168. if (template == null || template.isEmpty()) {
169. template = "/apps/trainingproject/templates/page-content";
170. }
171.
172. //Create page
173. PageManager pageManager = resource.getResourceResolver().adaptTo(PageManager.class);
174.
175. if (pageManager != null && !parentPath.isEmpty() && !pageNodeName.isEmpty()) {
176. Page p = pageManager.create(parentPath,
177. pageNodeName,
178. template,
179. title);
180.
181. //Add a tag to the page
182. if (tag != null && !tag.isEmpty()) {
183. //TagManager can be retrieved via adaptTo
184. TagManager tm = resource.getResourceResolver().adaptTo(TagManager.class);
185. if (tm != null) {
186. tm.setTags(p.getContentResource(),
187. new Tag[]{tm.resolve(tag)},
188. true);
189. }
190. }
191.
192.
193. //Publish page if requested
194. boolean publishPage = Boolean.parseBoolean(publish);
195. if (publishPage) {
196. //Replicator is exposed as a service
197. replicator.replicate(resource.getResourceResolver().adaptTo(Session.class),
198. ReplicationActionType.ACTIVATE,
199. p.getPath());
200. }
201.
202. pageInfo.put("Status", "Successful");
203. pageInfo.put("Location", p.getPath());
204. pageInfo.put("Title", p.getTitle());
205. pageInfo.put("Template Used", p.getTemplate().getPath());

Extend and Customize Adobe Experience Manager 312


206. pageInfo.put("Tagged with", p.getTags()[0].getTitle());
207. pageInfo.put("Was Published", String.valueOf(publishPage));
208. }
209. }
210.
211. if(pageInfo.isEmpty()) {
212. pageInfo.put("Status", "Could not create a page");
213. }
214. return pageInfo;
215. }
216. }

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 313


Task 2: Deploy and test the CSV page creator

1. In Project Explorer, right-click training.core and select Run As > Maven install. The build starts

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: Now that your new servlet accepts and processes CSV files, you can test with the cURL
command.

3. Copy the command from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/pagecreator-curl-


command.txt.

4. Open your command terminal, and type the location of CSV files, as follows:

CD C:\Exercise_Files\12_Deep_Dive_into_AEM_APIs

Extend and Customize Adobe Experience Manager 314


5. Try the servlet with bad data first. Execute the following statement, as shown:

curl -u admin:admin -X POST http://localhost:4502/content/pagecreator.csv.json -F


[email protected]

ADOBE COPYRIGHT PROTECTED


NOTE: If you are on a Mac, you can format the returned json by adding | json_pp.

6. Verify that all the pages are not created in AEM: Sites > TrainingProject Site > click the thumbnail on
English page, as shown:

Extend and Customize Adobe Experience Manager 315


7. Try the servlet with well formatted data. Execute the following statement, as shown:

 curl -u admin:admin -X POST http://localhost:4502/content/pagecreator.csv.json -F


[email protected]

ADOBE COPYRIGHT PROTECTED


8. Verify the newly created pages in AEM: Sites > TrainingProject Site > English > Products, as shown:

NOTE: If you want to use the browser rather than cURL, you can install the http-pagecreator.zip content
package (from the exercise folder) and do a request for http://localhost:4502/content/pagecreator.csv.html.

Extend and Customize Adobe Experience Manager 316


AEM Projects
The AEM Projects feature helps manage and group all workflows and tasks associated with
creating content in AEM Sites and Assets.
Project Template

AEM Projects comes with several out-of-the box (OOTB) project templates. When creating a new
project, authors can choose from these available templates. For large AEM implementations with
unique business requirements, developers can create custom project templates. With these custom
templates, developers can configure the project dashboard, hook into custom workflows, and
create additional business roles for a project.
You should create project templates under source control, and they should be beneath your

ADOBE COPYRIGHT PROTECTED


application folder under /apps. Ideally, they should be placed in a subfolder with the naming
convention of */projects/templates/<my-template>. This naming convention ensures that new
custom templates will automatically become available to authors when creating a project.
The configuration of available project templates is set at the /content/projects/jcr:content node by
the cq:allowedTemplates property. By default, this is a regular
expression: /(apps|libs)/.*/projects/templates/.*
The root node of a project template will have jcr:primaryType of cq:Template. Beneath the root
node, there are three nodes: gadgets, roles, and workflows. These nodes are all nt:unstructured.
• Gadgets - The child nodes of the gadgets node control which project tiles populate the project's
dashboard when you create a new project. The project tiles are simple cards that populate the
workplace of a project.

• Roles - There are three default roles for every project: Observers, Editors, and Owners. By adding child
nodes beneath the roles node, you can add additional business specific project roles for the template.
You can then tie these roles to specific workflows associated with the project.

• Workflows - Workflows enable you to automate AEM activities. Workflows consist of a series of steps
that are executed in a specific order. Each step performs a distinct activity, such as activating a page or
sending an email message. One of the reasons for creating a custom project template is that it gives
you the ability to configure the available workflows for the project. These can be OOTB workflows or
custom workflows. Beneath the workflows node there needs to be a models node (nt:unstructured)
and a child node to specify the available workflow models.

The root node of the project template will be of type cq:Template. On this node, you can configure
the jcr:title and jcr:description properties that will be displayed in the Create Project wizard. There
is also a property called wizard that points to a form that will populate the properties of the
project.

Extend and Customize Adobe Experience Manager 317


Exercise 4: Create a simple project
In this exercise, you will create a simple project.

1. In AEM, go to the Navigation screen and click Projects, as shown:

ADOBE COPYRIGHT PROTECTED


The Projects console opens, as shown:

2. In the Projects console, click Create > Project. The Create Project page opens.

Extend and Customize Adobe Experience Manager 318


3. Select Simple Project from the template, as shown:

ADOBE COPYRIGHT PROTECTED


4. Click Next. The Properties window opens.

5. Enter the following values for the simple project, as shown:

a. Enter My Simple Project in the Title field.

b. Enter Carlene Avery in the User field, ensure the dropdown beside it is set to Editors, and click
Add.

c. Enter Iris Mccoy in the User field, ensure the dropdown beside it is set to Observers, and click
Add.

Extend and Customize Adobe Experience Manager 319


ADOBE COPYRIGHT PROTECTED
6. Click Create in the top right corner. The Success pop-up window opens.

7. Click Open. The project is created, as shown:

Extend and Customize Adobe Experience Manager 320


8. Observe different types of tiles, as shown:

• Team enables you to add or edit members of this project.


• Assets is a working area for assets in this project.
• Workflows enables you to run project workflows based on the team for this project.
• Experiences are all the pages that are being used in this project.
• Project info shows all the metadata information of this project.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 321


Executing an Existing Workflow
AEM comes with a set of predefined workflows that perform common actions. You can
customize those workflows if the built-in steps do not include all the necessary tasks. You
can execute a workflow from any of the following places:
• Context menu

• Workflow console

• Site Admin (classic UI)

• Sidekick (classic UI)

Depending on the type of workflow, the workflow goes through a series of steps until its completion.

ADOBE COPYRIGHT PROTECTED


Defining Workflow Models

Based on your business requirements, you can modify an existing workflow or create a new
one. You can use the Workflow console to manage workflow models and launchers. A
workflow model includes a Flow Start and a Flow End step. These steps indicate the
beginning and end of the workflow.

In AEM, there are a number of steps available for workflows, such as Participant, Process,
Create Task, Delete Node, Dialog Participant, Dynamic Participant, and Form Participant.
Each step can contain any number of actions and associated conditions. For example, a step
in a publish workflow may involve the step- approval from an editor. Two of the most
commonly used workflow steps are Participant and Process.
Participant Step

The Participant step requires manual intervention by a person to advance the workflow. It
enables you to assign a step to a user or a group of users. If the workflow is assigned to just
one user, that user needs to complete that task before the workflow can proceed to the next
step. If the workflow is assigned to a group of users, all those users need to complete the
task.
You can notify participants of their required action through email. Also, if configured, the
participants will receive an email notification when the workflow is completed or if the
workflow is terminated.
You can configure timeouts and timeout handlers for this step. Timeout is the period after
which the step will be timed out. You can select between off and immediate, and if you want
to specify specific blocks of time, you can select 1h, 6h, 12h, and 24h. The timeout handler
controls the workflow when the step times out.
Every new model created includes a sample participant step, which you can either edit or
remove. You can add and configure additional steps as required.

Extend and Customize Adobe Experience Manager 322


Process Step

The Process step involves automatic actions that are executed by the system if specific conditions are
met. This step:

• Executes an ECMA script or calls an OSGi service to automate the process.

• Offers the following built-in processes:

o Workflow control processes: Control the behavior of the workflow and do not perform any action on
content

o Basic processes: Delete a nod, or logs a debug message

WCM processes: Perform WCM-related tasks, such as activating a page or confirming registration

ADOBE COPYRIGHT PROTECTED


o

o Versioning processes: Perform version-related tasks, such as creating versions of the payload

o DAM processes: Perform DAM-related tasks, such as creating thumbnails, creating sub-assets, and
extracting metadata

o Collaboration processes: Are related to the collaboration features of AEM, such as the collabration
with social communities.

Developing Custom Steps

You can extend workflow steps with scripts to provide more functionality and control. You can create
customized process steps by using the following methods:

• Java class bundles: Create a bundle with the Java class, and then deploy the bundle into the OSGi
container by using the Web console.

• ECMA scripts: Scripts are located in JCR under etc/workflows, and they are executed from there. To
use a custom script, create a new script with the extension .ecma under the same folder. The script
will then show up in the process list for a process step.

Creating a Workflow

To create a workflow:

1. Open the Workflow console, and create a new model.

2. Double-click the newly created model, and modify the steps by clicking and dragging the required
workflow steps from the sidekick to the workflow.

3. Edit the properties of the steps.

4. Save the workflow.

Extend and Customize Adobe Experience Manager 323


Workflow Launchers

The Workflow launcher enables you to invoke a workflow based on certain predefined conditions.
These conditions are based on changes to the content located in JCR. For example, when a page is
modified, it can trigger a workflow.

You can configure the workflow launchers through the Workflow console,
(http://localhost:4502/libs/cq/workflow/admin/console/content/launchers.html), as shown:

ADOBE COPYRIGHT PROTECTED


For example, to publish a page after it is modified, you will use the following values for the properties:

• Event type: modified: Type of event that triggers the workflow

• Node type: nt:unstructured: Type of node that is affected by the workflowPath: /content/Geometrixx:
Property that indicates the path of the node

• Workflow: PublishPage: Property that indicates the workflow to be executed when the event occurs

• Activate: Enable: Property that controls whether the launcher should be activated

• Run mode (s): Author: Property that indicates the type of server to which the launcher applies

Extend and Customize Adobe Experience Manager 324


The following table differentiates Workflow Launchers and JCR Observations or Sling events:
Workflow Launcher JCR Observation / Sling Eventing

Has a UI Does not have a UI

Easy to start or stop Requires the use of Web Console to stop the
listener component

The workflow model can be changed dynamically Requires programmatic or configuration changes

Involves more overhead, and is ideal to use if there Involves less overhead and can handle more
are only moderate amounts of event expected frequent events

It is cluster aware and runs only on the cluster Sling eventing is not cluster aware

ADOBE COPYRIGHT PROTECTED


master

Extend and Customize Adobe Experience Manager 325


Exercise 5: Customize the process step
In this exercise, you will customize the process step in the workflow. This exercise has five tasks:

1. Install the custom project and custom workflow content package

2. Create the custom worklflow process

3. Deploy the class

4. Customize the workflow

5. Test the workflow

Task 1: Install the content package

ADOBE COPYRIGHT PROTECTED


1. Login to AEM and navigate to CRXDE lite.

2. Install the content package \Exercise_Files\12_Deep Dive into AEM APIs\ training-project-template.zip
with the completed project template.

3. Install the content package \Exercise_Files\12_Deep Dive into AEM APIs\ training-project-workflow.zip.

4. Verify the content packages are successfully installed:

NOTE: If you want to learn more on how to create a project template, execute and build a workflow from
scratch, please refer to Appendix-Creating Projects&Worflows of this course.

Extend and Customize Adobe Experience Manager 326


Task 2: Create the custom workflow process

1. Double-click the Eclipse shortcut on the desktop. The Eclipse Launcher opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


4. Copy the file ApprovalStatusWriter.java from /Exercise_Files/12_Deep_Dive_into_AEM_APIs/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


12_Deep_Dive_into_AEM_APIs /. Copy and paste the file from the exercise files referenced
to ApprovalStatusWriter.java to Eclipse. Please do not copy the code from this student
guide. The code in the student guide is for illustrative purposes only.

5. In Eclipse, right-click com.adobe.training.core. and paste the file. The ApprovalStatusWriter.java class is
created, as shown:

Extend and Customize Adobe Experience Manager 327


6. Double-click ApprovalStatusWriter.java. The file opens in the editor.

1. package com.adobe.training.core;
2.
3. import com.adobe.granite.workflow.WorkflowSession;
4. import com.adobe.granite.workflow.WorkflowException;
5. import com.adobe.granite.workflow.exec.WorkItem;
6. import com.adobe.granite.workflow.exec.WorkflowProcess;
7. import com.adobe.granite.workflow.exec.WorkflowData;
8. import com.adobe.granite.workflow.metadata.MetaDataMap;
9.
10. import org.apache.sling.api.resource.ModifiableValueMap;
11. import org.apache.sling.api.resource.PersistenceException;
12. import org.apache.sling.api.resource.Resource;
13. import org.apache.sling.api.resource.ResourceResolver;

ADOBE COPYRIGHT PROTECTED


14. import org.osgi.service.component.annotations.Component;
15. import org.osgi.framework.Constants;
16.
17. @Component(service = WorkflowProcess.class,
18. property = {Constants.SERVICE_DESCRIPTION + "=Workflow process to set status to approved",
19. Constants.SERVICE_VENDOR + "=Adobe",
20. "process.label=Approval Status Writer"
21. })
22.
23. public class ApprovalStatusWriter implements WorkflowProcess {
24.
25. private static final String TYPE_JCR_PATH = "JCR_PATH";
26.
27. @Override
28. public void execute(WorkItem item, WorkflowSession workflowSession, MetaDataMap args) throws WorkflowExceptio
n{
29. WorkflowData workflowData = item.getWorkflowData();
30. if (workflowData.getPayloadType().equals(TYPE_JCR_PATH)) {
31. String path = workflowData.getPayload().toString() + "/jcr:content";
32. try (ResourceResolver rr = workflowSession.adaptTo(ResourceResolver.class)){
33. Resource resource = rr.getResource(path);
34. resource.adaptTo(ModifiableValueMap.class).put("approved", readArgument(args));
35. rr.commit();
36. }
37. catch (PersistenceException e ) {
38. throw new WorkflowException(e.getMessage(), e);
39. }
40. }
41. }
42.
43. private static boolean readArgument(MetaDataMap args) {
44. String argument = args.get("PROCESS_ARGS", "false");
45. return argument.equalsIgnoreCase("true");
46. }
47. }
48.

Extend and Customize Adobe Experience Manager 328


Task 3: Deploy the class

1. In Project Explorer, right-click training.core and choose Run As > Maven install. The build starts.

2. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 329


Task 4: Create the custom workflow process

1. In the Workflow Models window, select Dynamic Editing, Approval, and Publish and click Edit, as
shown:

ADOBE COPYRIGHT PROTECTED


The Workflow Model Dynamic Editing, Approval, and Publish opens for editing, as shown:

Extend and Customize Adobe Experience Manager 330


2. Drag the Process Step component from the left-hand side into the model, as shown:

ADOBE COPYRIGHT PROTECTED


3. Double-click Process Step. The Process – Step Properties dialog box opens.

4. Select the Common tab and enter the title:


• Title: Approval Status Writer

5. Select the Process tab and enter the following details, as shown:
• Name: Approval Status Writer
• Select the Handler Advance checkbox.

Extend and Customize Adobe Experience Manager 331


6. Click the Done icon (checkmark) on the top of the dialog box to save the changes.

7. At this point the workflow is complete. In the top-right click Sync, as shown. This will write
the workflow model.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 332


Task 5: Test the Workflow

1. To test the new project, navigate to the AEM > Projects. Select Create > Project. The template opens.

2. Click Training Publishing Project, and click Next. The Create Project window opens.

3. Enter the values for the project, as shown:

• Enter Pages for Publishing for the Title field.

• Enter Carlene Avery in the User field, ensure the dropdown beside it is set to Editors, and click Add.

ADOBE COPYRIGHT PROTECTED


• Enter Iris Mccoy in the User field, ensure the dropdown beside it is set to Reviewers, and click Add.

4. Click Create in the top right corner. The Success pop-up window opens.

Extend and Customize Adobe Experience Manager 333


5. Click Open in the pop-up window. Observe the changes of the custom project.

ADOBE COPYRIGHT PROTECTED


6. Click the dropdown on the Workflow tile and select Start Workflow. The Start Workflow page opens,
as shown:

Extend and Customize Adobe Experience Manager 334


7. Select Dynamic Editing, Approval and Publish and click Next, as shown: The Properties
window opens.

ADOBE COPYRIGHT PROTECTED


8. Enter the following details, as shown:

• Title: Publish the Experience Page

• Assign To: Carlene Avery

• Content Path: /content/we-retail/language-masters/en/experience

Extend and Customize Adobe Experience Manager 335


9. Click Submit at the top right corner. The workflow is built.

NOTE: At this point, you should be able to walk through the edit/approval steps. Even though the
tasks are assigned to different project groups, the admin can see and run through all the tasks.

10. Refresh the browser and notice the new Task, as shown:

ADOBE COPYRIGHT PROTECTED


11. Click the ellipsis on the Task Tile. The Inbox opens, as shown:

Extend and Customize Adobe Experience Manager 336


12. Find the new message in the Inbox, as shown:

13. Select Edit the Page, and click Complete, as shown. The Complete Task dialog box appears.

ADOBE COPYRIGHT PROTECTED


14. Enter a comment and click Complete.

15. Notice there is an Approval Task, as shown:

16. Revert to the admin user.

Extend and Customize Adobe Experience Manager 337


17. Impersonate Carlene Avary, and click OK, as shown:

ADOBE COPYRIGHT PROTECTED


18. Select the Approval Task, and click Complete. The Complete Task pop-up window appears. Notice there
is a drop-down with Approved and Denied.

If you select Denied, the workflow will step back and create new task and re-edit the page with the
comments of the reviewer (cavery).
If you select Approved, the workflow will move forward.

19. Complete the task by selecting Approved and add a comment Approved. Click Complete.

20. Verify the page is published, as shown:

Extend and Customize Adobe Experience Manager 338


21. In CRXDE Lite, navigate to /content/we-retail/language-masters/en/experience/jcr:content and observe
the jcr-content properties, as shown:

ADOBE COPYRIGHT PROTECTED


NOTE: Notice a new property named approved is added to the list of properties.

Extend and Customize Adobe Experience Manager 339


References
For more information on AEM Projects, go to:
https://helpx.adobe.com/experience-manager/kt/platform-repository/using/projects-part1-tutorial-
develop.html

Task Management API- https://helpx.adobe.com/experience-manager/6-


4/sites/developing/using/reference-materials/javadoc/com/adobe/granite/taskmanagement/package-
summary.html

Task Object - https://helpx.adobe.com/experience-manager/6-4/sites/developing/using/reference-

ADOBE COPYRIGHT PROTECTED


materials/javadoc/com/adobe/granite/taskmanagement/Task.html

Main Granite Workflow API - https://helpx.adobe.com/experience-manager/6-


4/sites/developing/using/reference-materials/javadoc/com/adobe/granite/workflow/package-
summary.html

Granite Workflow Objects - https://helpx.adobe.com/experience-manager/6-


4/sites/developing/using/reference-materials/javadoc/com/adobe/granite/workflow/exec/package-
summary.html

Extend and Customize Adobe Experience Manager 340


Module 13: Managing Users, Groups, and
Rights in AEM

Introduction

Organizations can use Adobe Experince Manager (AEM) to manage projects, workflows, assets, forms,
communities and integrations, and build websites and mobile apps. However, they need to ensure that

ADOBE COPYRIGHT PROTECTED


their data, content, documents, and projects are secured from unauthorized internal and external users. In
AEM, this is achieved by creating users, groups, and setting-specific access rights to the user account.
Setting permissions on users and groups allow you to control how the users and groups can access
resources in AEM.

Objectives

• Explain users, groups, and access control lists

• Create users and groups

• Add users to a group

• Explain permissions and access rights

• Create a custom AEM project to modify permissions

Extend and Customize Adobe Experience Manager 341


Working with Users, Groups, and Access Control Lists
You can configure and manage user authentication and authorization within AEM.
Users and Groups

A user refers to either a human user or an external system connected to the system. The user
account stores the user details needed for accessing AEM. Each user account is unique, and stores
the basic account details and privileges assigned.
Groups are collections of users and/or other groups. Their primary purpose is to simplify the
maintenance process by reducing the number of entities to be updated, as a change made to a
group is applied to all the members of the group.

ADOBE COPYRIGHT PROTECTED


Enabling access to a CRX repository involves several topics:
• Access Rights

• User Administration

• Group Administration

• Access Right Management

Access Right Management is important, especially with different users accessing and performing
different tasks within AEM. With the access control tab in CRXDE Lite, you can configure access
control policies and assign their corresponding privileges.

Extend and Customize Adobe Experience Manager 342


Permissions and ACLs
AEM uses Access Control Lists (ACLs) to determine what actions a user or a group can take, and where it
can perform those actions.
Permissions define who can perform which actions on a resource. The permissions are applied after
evaluating ACLs. You can change the permissions granted or denied to a given user by selecting or
clearing the checkboxes for the individual AEM actions. A checkmark indicates the user can perform that
action. Whereas, no checkmark indicates the user is denied permission to perform that action.
The screenshot depicts the permissions for users and groups:

ADOBE COPYRIGHT PROTECTED


The location of the checkmark in the grid also indicates what permissions the users have in which
locations within AEM.
The permissions are also applied to any child pages. If a permission is not inherited from the parent
node, but has at least one local entry for it, the specific symbols are appended to the checkbox. A local
entry is created in the content repository interface.
The following table describes the symbols appended to the checkbox for an action at a given path:

* (asterisk) Indicates that there is at least one local entry (either effective or
ineffective). These wildcard ACLs are defined in the content repository.
! (exclamation mark) Indicates that the permission is inherited, that is, there is at least one entry
that currently has no effect.
When you hover over the asterisk or exclamation mark, a tool tip provides more details about the
declared entries. The following table describes the two parts of the tool tip:

Upper part Lists the effective entries.


Lower part Lists the non-effective entries that may have an effect somewhere else in
the tree.

Extend and Customize Adobe Experience Manager 343


The screenshot shows the allow and disallow permissions for contents:

ADOBE COPYRIGHT PROTECTED


Actions

You can perform actions on a page (resource). For each page in the hierarchy, you can specify the action
the user can take on that page. Permissions enable you to allow or deny an action. The following table
describes the actions:

Action Description

Read The user can read the page and all the child pages.
Modify The user can modify the existing content on the page and on all the child pages. The
user can create new paragraphs on the page or any child page. At the JCR level, users
can modify a resource by modifying its properties, locking, visioning, and nt-
modifications, and they have complete write permission on nodes defining a jcr:content
child node, such as cq:Page, nt:file, and cq:Asset.

Create The user can create a new page or child page. If the Modify action is denied, the
subtrees below jcr:content are specifically excluded as the creation of jcr:content and its
child nodes are considered a page modification. This only applies to nodes defining a
jcr:content child node.

Delete The user can delete the existing paragraphs from the page or any child page. If the
Modify action is denied, any subtrees below jcr:content are specifically excluded as
removing jcr:content and its child nodes is considered a page modification. This only
applies to nodes defining a jcr:content child node.

Read ACL The user can read the ACL of the page or child pages.

Extend and Customize Adobe Experience Manager 344


Edit ACL The user can modify the ACL of the page or any child pages.

Replicate The user can replicate content to another environment. For example, the user can
replicate content from an author instance to a pubic instance. This privilege is also
applied to any child pages.

Evaluating ACLs

AEM WCM uses ACLs to organize the permissions being applied to various pages. ACLs are made up of
individual permissions and are used to determine the order in which these permissions are applied. The
list is formed per the hierarchy of the pages under consideration. This list is then scanned bottom-up

ADOBE COPYRIGHT PROTECTED


until the first appropriate permission to apply to a page is found.

Concurrent Permission on ACLs

When two concurrent (and opposing) permissions are listed on the same ACL for the same resource, the
permission at the bottom is applied to the resource.

If a user is part of the two groups allowed-it and restricted-it, you can see how access to the page
products is denied because the ACL deny in read access is the rule at the bottom.

If the order of the ACL is the opposite and a user is part of two groups, allowed-it and restricted-it, the
user will have access to the products page because the ACL allow in read access rule is at the bottom.

Extend and Customize Adobe Experience Manager 345


Exercise 1: Create a new user
In this exercise, you will add yourself as a user in AEM.

1. Open the Navigation console, or click the link: http://localhost:4502

2. Click Adobe Experience Manager in the upper left of the screen, and then click the Tools icon, as
shown:

ADOBE COPYRIGHT PROTECTED


3. Click Security > Users. The User Management page is displayed.

Extend and Customize Adobe Experience Manager 346


4. Click Create User from the actions bar. The Create New User wizard is displayed.

5. Enter your details such as your ID, email address, password, first name, and last name, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 347


6. Click Save & Close.

The success message displays on the User Management page, as shown:

ADOBE COPYRIGHT PROTECTED


The user you created is added to the User Management list, as shown:

Extend and Customize Adobe Experience Manager 348


Exercise 2: Create a new group
In this exercise, you will create a new group named Legal.

1. Navigate to the Projects console, or click the link: http://localhost:4502

2. Click Adobe Experience Manager at the top left corner of the screen, and then click the Tools icon.

3. Click Security > Groups. The Group Management page is displayed.

ADOBE COPYRIGHT PROTECTED


4. Click Create Group from the actions bar. The Create New Group wizard is displayed.

Extend and Customize Adobe Experience Manager 349


5. Enter the group details such as id, name, and description, as shown:

ADOBE COPYRIGHT PROTECTED


6. Click Save & Close. The group is created.

The success message is displayed on the Group Management page, as displayed below:

The group that you created will be added to the groups list, as shown:

Extend and Customize Adobe Experience Manager 350


Exercise 3: Add the user to the group
In this exercise, you will add yourself to the Legal group.

1. Navigate to the Projects console, or click the link: http://localhost:4502

2. Click Adobe Experience Manager in the upper left corner of the screen, and then click the Tools icon.

3. Click Security > Groups. The Group Management page is dispalyed.

ADOBE COPYRIGHT PROTECTED


4. Look for the legal group from the list, and then click the Legal. The Edit Group Settings wizard appears.

5. Select the Members tab. The Add Members to this Group appears.

6. Select cgrant from the drop-down list, as shown:

7. Click Save & Close. The success message is displayed on the Group Management page

8. Look for the Contributors group from the list, and then click the Contributors. Edit Group Settings wizard
appears.

Extend and Customize Adobe Experience Manager 351


9. In the Add Members to Group section, select the Legal group and click Save & Close.

10. Click Adobe Experience Manager on the upper left corner of the screen, and then
click the Tools icon.

11. Click Security > Permissions. The Security window opens.

12. Double-click your ID (for example, cgrant) in the left pane to view various properties,
groups, and permissions associated with the user in the right pane.

ADOBE COPYRIGHT PROTECTED


13. From the right pane, click the Groups tab. You will notice the user is given access to
the Legal and Contributors groups.

14. Open the Permissions tab.

The screenshot below shows the weretail folder under “apps” with “read” access selected:

Extend and Customize Adobe Experience Manager 352


Exercise 4: Create a custom AEM project with ACL automation
In this exercise, you will automate permission customization for a custom AEM project and
workflow. The JCR API is a low-level API that allows you to customize the repository and create
nodes and properties. Most operations in AEM can be coded by Sling or AEM APIs more effectively
rather than using the JCR API. You will use the JCR API to programmatically change the
permissions for a user within a workflow in this exercise. This exercise has the following four tasks:

1. Create a custom workflow process


2. Add the workflow process to a project workflow
3. Increase permissions on the workflow process user
4. Test the permissions automation with an AEM project

ADOBE COPYRIGHT PROTECTED


Use Case: A project manager wants to assign work (pages) to their team without involving IT. And
once the work is completed, the page will then go through an approval process. If it is approved, it
will be published, otherwise it will go back to the editor with comments, for a proper feedback
loop. A typical author has read access to their website and write permissions are given only on
request. Authors should only have access to the pages they are currently assigned.
Solution: Create a custom project for each PM that allows them to add team members for editing
and reviewing. When new work needs to be assigned:
i. The PM will start a project workflow instance with the work (or payload) and complete
details of the work to be done. The PM will also specify the editor that is assigned to the
work.
ii. The workflow will then assign edit permissions to the editor and notify the editor with a
task that contains the details of the work to be done.
iii. After the editor has completed their task, a new task is assigned to the Reviewers group. If
they approve the work done, the page is published. If they deny the work, they can
comment, and the workflow will be stepped back to the editor to make the proper
changes.

Extend and Customize Adobe Experience Manager 353


Task 1: Create a custom workflow process

1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher opens.

2. Specify the Workspace as C:\Workspace and click Launch. The Workspace opens.

3. In Project Explorer, navigate to training.core > src/main/java, as shown:

ADOBE COPYRIGHT PROTECTED


4. Copy the file AutoAssignACL.java from /Exercise_Files/13_Automate_ACL_with_JCR_API/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


13_Automate_ACL_with_JCR_API /. Copy and paste the file from the exercise files referenced to
AutoAssignACL.java to Eclipse. Please do not copy the code from this exercise book. The code in the
student guide is for illustrative purposes only.

5. In Eclipse, right-click com.adobe.training.core and paste the file. The AutoAssignACL.java class is created,
as shown:

Extend and Customize Adobe Experience Manager 354


6. Double-click AutoAssignACL.java. The file opens in editor.

1. package com.adobe.training.core;
2.
3. import javax.jcr.security.AccessControlList;
4. import javax.jcr.security.AccessControlManager;
5. import javax.jcr.security.Privilege;
6. import javax.jcr.Session;
7.
8. import org.osgi.service.component.annotations.Component;
9.
10. import org.apache.jackrabbit.api.security.user.Authorizable;
11. import org.apache.jackrabbit.api.security.user.UserManager;
12. import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
13. import org.apache.sling.jcr.base.util.AccessControlUtil;

ADOBE COPYRIGHT PROTECTED


14. import org.osgi.framework.Constants;
15. import org.slf4j.Logger;
16. import org.slf4j.LoggerFactory;
17.
18. import com.adobe.granite.workflow.WorkflowException;
19. import com.adobe.granite.workflow.WorkflowSession;
20. import com.adobe.granite.workflow.exec.WorkItem;
21. import com.adobe.granite.workflow.exec.WorkflowProcess;
22. import com.adobe.granite.workflow.metadata.MetaDataMap;
23. import com.adobe.granite.workflow.exec.WorkflowData;
24.
25. /**
26. * This is a project workflow process step. When starting a workflow from a project, you can specify the:
27. *
28. * Payload: The page that will have 'modify' permissions added/removed from its ACL
29. * User: The user attached to the ACL
30. *
31. * This process will add/remove jcr privileges that correlate to 'Modify, Create, Delete' in the /useradmin
32. * for the given user on the payload. An argument is used to specify to add or remove 'modify' permissions.
33. * The argument must follow permission=<add || remove>
34. */
35. @Component(service = WorkflowProcess.class,
36. property = {Constants.SERVICE_DESCRIPTION + "=A sample workflow process implementation.",
37. Constants.SERVICE_VENDOR + "=Adobe Digital Learning Services",
38. "process.label=Auto Assign Edit Permissions"
39. })
40.
41. public class AutoAssignACL implements WorkflowProcess{
42.
43. private final Logger logger = LoggerFactory.getLogger(getClass());
44.
45. /**
46. * @param item - Holds the payload and user
47. * @param workflowSession - Session with service user (workflow-process-
service) that will be modifying the permissions
48. * @param workflowArgs - permission=add will add 'modify' permissions. permission=remove will remove 'modify'
permissions
49. */
50. @Override
51. public void execute(WorkItem item, WorkflowSession workflowSession, MetaDataMap workflowArgs) throws Work

Extend and Customize Adobe Experience Manager 355


flowException {
52.
53. WorkflowData workflowData = item.getWorkflowData(); //get the workflow properties
54. String userID = workflowData.getMetaDataMap().get("assignee", String.class);
55. logger.info("User to add permissions: " + userID);
56. String payload = workflowData.getPayload().toString();
57. logger.info("Content path to add permissions: " + payload);
58.
59. UserManager uM;
60. try {
61. Session jcrSession = workflowSession.adaptTo(Session.class);
62. //The user that actually modifies the permissions is the workflow-process-service
63. //The workflow-process-service must have Read ACL and Write ACL permissions for the payload
64. logger.info("Service user to change permissions: " + jcrSession.getUserID());
65.
66. uM = AccessControlUtil.getUserManager(jcrSession);

ADOBE COPYRIGHT PROTECTED


67. //Get the Authorizable object for the new user
68. Authorizable authorizable = uM.getAuthorizable(userID); //this might be a user or a group
69.
70. AccessControlManager accessControlManager = jcrSession.getAccessControlManager();
71.
72. //JCR privileges that encompass AEM Permissions: "Modify, Create, Delete" in the /useradmin
73. Privilege[] privileges = {accessControlManager.privilegeFromName(Privilege.JCR_MODIFY_PROPERTIES),
74. accessControlManager.privilegeFromName(Privilege.JCR_LOCK_MANAGEMENT),
75. accessControlManager.privilegeFromName(Privilege.JCR_VERSION_MANAGEMENT),
76. accessControlManager.privilegeFromName(Privilege.JCR_REMOVE_CHILD_NODES),
77. accessControlManager.privilegeFromName(Privilege.JCR_REMOVE_NODE),
78. accessControlManager.privilegeFromName(Privilege.JCR_ADD_CHILD_NODES),
79. accessControlManager.privilegeFromName(Privilege.JCR_NODE_TYPE_MANAGEMENT)};
80.
81. //Get the ACL for the payload
82. AccessControlList acl = AccessControlUtils.getAccessControlList(jcrSession, payload);
83. //Add the user/privileges to the payload ACL
84. acl.addAccessControlEntry(authorizable.getPrincipal(), privileges);
85.
86. //Based on the process step arguments, permissions are either added or removed
87. //Assumes arguments are given as: permission=add
88. String arguments = workflowArgs.get("PROCESS_ARGS", "");
89. if(arguments.contains("permission")){
90. String permission = arguments.split("=")[1];
91. if(permission.equals("add")){ //Adds permissions to edit the payload
92. logger.info("Adding permissions");
93. accessControlManager.setPolicy(payload, acl);
94. logger.info("Added modify permissions to " + payload + " for " + userID);
95. }else if(permission.equals("remove")){ //Removes permissions to edit the payload
96. logger.info("Removing permissions");
97. accessControlManager.removePolicy(payload, acl);
98. logger.info("Removed modify permissions to " + payload + " for " + userID);
99. }
100. }else{
101. //do nothing, if the workitem argument is not set
102. logger.info("No arguments given for workitem");
103. }
104.
105. jcrSession.save();
106. } catch (Exception e) {

Extend and Customize Adobe Experience Manager 356


107. logger.error(e.getMessage(), e);
108. e.printStackTrace();
109. }
110. }
111. }

7. In Project Explorer, right-click training.core and choose Run As > Maven install. The build starts.

8. Verify the bundle installed successfully, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 357


Task 2: Add the workflow process to a project workflow

1. In a previous module (Deep_Dive_into_AEM_APIs) , you created a custom project and custom workflow.
If you do not have these exercises completed, install:

• training-project-template.zip

• training-project-workflow.zip

NOTE: Both of these files can be found in the exercises_folder.

ADOBE COPYRIGHT PROTECTED


2. Open the workflow Dynamic Editing, Approval and Publish by selecting the new workflow and clicking
Edit. The workflow opens.

3. Drag and drop Process Step from the Workflow section to the area with the dotted line Drop steps or
participants here, as shown:

4. Double-click to open the Process Step.

5. Select the common tab:

Title: Add Edit Permissions

Extend and Customize Adobe Experience Manager 358


6. Select the Process tab:

Process: Auto Assign Edit Permissions (created when you added the AutoAssignACL.java class)

Select the Handler Advance checkbox.

Arguments: permission=add

ADOBE COPYRIGHT PROTECTED


7. Click the Done icon (checkMark) to save the changes.

8. Add another Process step from the Workflow section below the Approval Status Writer.

• Title: Remove Edit Permissions

9. Select the Process tab:

Process: Auto Assign Edit Permissions

Select the Handler Advance checkbox.

Arguments: permission=remove

Extend and Customize Adobe Experience Manager 359


10. Click CheckMark to save the changes.

11. Click Sync to save the workflow.

12. Verify your completed workflow looks similar to the picture, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 360


Task 3: Increase permissions on the workflow process user

Because our Java class implements the Workflow Process service, the workflow-process-service service
user is the user that updates permissions for our editors. To allow the workflow-process-service to do
this, you need to give workflow-process-service the Read and Edit ACL permissions.

1. Open URL http://localhost:4502/useradmin. The AEM Security console opens.

2. In the AEM Security console, serach for the user named workflow-process-service, as shown:

ADOBE COPYRIGHT PROTECTED


3. Select the Permissions tab for the workflow-process-service user.

Extend and Customize Adobe Experience Manager 361


4. Check Read ACL and Edit ACL, as shown:

ADOBE COPYRIGHT PROTECTED


5. Save the changes.

Extend and Customize Adobe Experience Manager 362


Task 4: Test the permissions automation with an AEM project

You now have your business process created that was outlined by your Project Manager. You have an
AEM Project template that uses our custom workflow that implements our auto assign ACL java class.
In this last step, you will run through the AEM Project as the Project Manager.

1. Impersonate Chuck Grant and open


http://localhost:4502/editor.html/content/we-retail/language-masters/en/men.html, as shown:

ADOBE COPYRIGHT PROTECTED


2. Observe the user Chuck Grant has only read permissions to the We.Retail Site.

3. Revert back to the admin user.

4. Navigate to Projects area - http://localhost:4502/projects.html, as shown:

Extend and Customize Adobe Experience Manager 363


5. Select Create > Project . The Create Project console opens.

6. Choose Training Publishing Project and click Next. The Project opens.

7. Enter the values for the project:

• Title: Page Editing and Approval

• Start Date: <Specify a date>

• Due Date: <Specify a date>

ADOBE COPYRIGHT PROTECTED


• User: Chuck Grant (Select Editors from drop-down list)

• Add another user: (by clicking Add )

• User: Carlene Avary (Select Reviewers from drop-down list)

8. Click Create. The Success pop-up window opens.

Extend and Customize Adobe Experience Manager 364


9. In the Success pop-up window, click Open.The Project opens, as shown:

ADOBE COPYRIGHT PROTECTED


10. In the Workflows Tile, Click Add Work. The Start Workflow screen opens.

11. In the Start Workflow screen, Select Dynamic Editing, Approval, and Publish and click Next.
The Properties screen opens.

12. In the Properties screen, enter the values, as shown:

• Title: Edit the Mens Page

• Assign To: Chuck Grant

• Content Path: /content/we-retail/language-masters/en/men

Extend and Customize Adobe Experience Manager 365


ADOBE COPYRIGHT PROTECTED
13. Click Submit. The Success pop-up window opens.

14. Click open. The project opens.

15. Refresh the browser and notice the new task, as shown:

16. Open another browser and log in as Chuck user.

Extend and Customize Adobe Experience Manager 366


17. Find the new message in the inbox, as shown:

18. Click View All (1 New) . The message is displayed.

ADOBE COPYRIGHT PROTECTED


19. Select Edit the Page and click View Payload, as shown:

20. Notice the page is in the workflow and you have the option to edit the page:

Extend and Customize Adobe Experience Manager 367


21. Click on the Title component called Featured Products. Notice all Edit actions are now enabled.

22. Click on the Wrench icon.

23. Make changes to the Title and click Done. The changes are saved.

24. Verify the page has updated with the changed you made.

ADOBE COPYRIGHT PROTECTED


25. Go back to the Inbox, select Edit the Page, and click Complete to complete the task.

Extend and Customize Adobe Experience Manager 368


26. Enter a comment and Complete. The task is completed.

27. Revert to the admin user.

28. Impersonate Carlene Avary.

29. Notice there is a new message in her inbox.

30. Click View All (1 New). The message is displayed.

ADOBE COPYRIGHT PROTECTED


31. Notice there is an Approval Task, as shown:

32. Click Complete and notice there is a dropdown with Approved and Denied.

If you select Denied, the workflow will step back and create new task for Chuck to re-edit the page
with the comments of the Reviewer (Carlene). Otherwise, if you select Approved, the workflow will
move forward.

Extend and Customize Adobe Experience Manager 369


33. Complete the task by selecting Approved and adding a comment. The task is approved.

34. Impersonate Chuck Grant.

35. Open the Men page (http://localhost:4502/editor.html/content/we-retail/language-


masters/en/men.html)

and notice you have only the read permissions as the edit permissions have been
removed, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 370


References
User administration and security:
https://docs.adobe.com/docs/en/aem/6-3/administer/security/security.html

User, group, and access rights:


https://docs.adobe.com/docs/en/aem/6-3/administer/security/user-group-ac-admin.html

ACL automation:
https://jackrabbit.apache.org/oak/docs/security/accesscontrol/editing.html

https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/security/AccessControlManager.html

ADOBE COPYRIGHT PROTECTED


https://www.programcreek.com/java-api-examples/?api=javax.jcr.security.AccessControlEntry

http://aempodcast.com/2017/apache-sling/sling-resource-api-vs-jcr-api/

https://www.slideshare.net/connectwebex/jcr-sling-or-aem-which-api-should-i-use-and-when

https://stackoverflow.com/questions/3908584/when-to-use-jcr-content-repository-over-other-options

Extend and Customize Adobe Experience Manager 371


Module 14: Writing Tests

Introduction

In Adobe Experience Manager (AEM), the testing frameworks, such as Maven, Mockito, Sling JUnit, and
Hobbes tests help automate the testing process. Unit tests in AEM can be set up and run very quickly
outside any container. Integration tests can run within an AEM instance. The idea of writing tests in
AEM is to detect defects as early as possible, ultimately reducing cost. This testing module is based on
how the AEM Archetype approaches testing in a real project.

ADOBE COPYRIGHT PROTECTED


Objectives

• Describe testing framework

• Explain the different types of testing

• Run unit tests and functional tests on your project

• Create unit tests using Mockito

• Create unit tests using Sling Mocks

• Create unit tests using AEM Mocks

Extend and Customize Adobe Experience Manager 372


Understanding Testing Frameworks
Software testing is done to ensure that the system performs as expected. The results of a test are
compared with the expected outcomes to validate the functionalities of the system. Some of these
tests can be extensive and repetitive. In such cases, you can use testing frameworks to automate
the testing process.
A testing framework is a system with a set of rules for automation of software testing. This system
makes use of function libraries, test data sources, object details, and various reusable modules.
The Mockito Framework

ADOBE COPYRIGHT PROTECTED


Mockito is an open source testing framework that allows the creation of mock objects in
automated unit tests. Mock testing frameworks effectively fake some external dependencies so
that the object being tested has a consistent interaction with its outside dependencies. Mockito
intends to streamline the delivery of these external dependencies that are not subjects of the test.
Features of Mockito:
• Mocks concrete classes as well as interfaces

• Verification errors are clean—click on stack trace to see failed verification in test. Click on
exception's cause to navigate to actual interaction in code. Stack trace is always clean.

• Allows flexible verification in order

• Supports exact-number-of-times and at-least-once verification

• Flexible verification or stubbing using argument matchers

• Allows creating custom argument matchers or using existing hamcrest matchers

Extend and Customize Adobe Experience Manager 373


Performing Unit Tests
In this module, you will look at the various unit tests that can be performed with AEM. Unit testing
is the process where the application is divided into units, and each unit is individually tested for its
successful operation.

Writing Sling Tests


You can use Sling to perform a number of tests, including:
• JUnit tests using OSGi bundles in an OSGi system

ADOBE COPYRIGHT PROTECTED


• Scriptable tests in a Sling instance, using any supported scripting language

• Integration tests against a Sling instance that is started during the Maven build cycle or
independently

Performing Sling-based Tests on the Server


To perform a Sling test, you need the org.apache.sling.junit.core bundle. This bundle provides a
service that allows bundles to register JUnit tests, and these tests are executed on the server by the
JUnitServlet registered by default at /system/sling/junit. You should also note that this bundle is
independent of Sling, and can work in other OSGi environments as well.
Performing Sling Scriptable Tests
To perform a Sling scriptable test, in addition to the above-mentioned bundle, you also need the
org.apache.sling.junit.scriptable bundle. While executing these tests, you need to make note of the
following:
• A node with the sling:Test mixin is a scriptable test node.

• Scriptable test nodes are executed only if they belong to the Sling’s ResourceResolver search path.
For example, /libs or /apps.

To learn more about Sling Testing, visit


https://sling.apache.org/documentation/development/sling-testing-tools.html.

Extend and Customize Adobe Experience Manager 374


ADOBE COPYRIGHT PROTECTED
You can also edit the test cases in a new browser tab by clicking the pencil icon in the toolbar
below a test suite. This opens the corresponding code file in CRXDE Lite, as shown:

Extend and Customize Adobe Experience Manager 375


Jenkins for Continuous Integration
Continuous integration is a process where all the development work is integrated to a system at a
predefined time, and is automatically tested and built. The purpose of using continuous integration
is to identify and catch errors early in the process.
Jenkins is an open source continuous integration tool used for build automation. Its main
functionality is to execute a predefined set of steps based on a trigger, such as a change in version
control system, or a time-based trigger, such as creating a build every 30 minutes.
The steps to be executed could be any of the following:
• Perform a software build with Apache Maven.

ADOBE COPYRIGHT PROTECTED


• Run a shell script.

• Archive the build result.

• Start the integration tests.

With Jenkins, you can:


• Monitor the execution of steps.

• Stop the process if any of the steps fail.

• Notify respective users of the status of the build, whether the build is a success or failure.

If you are working on an AEM project, here is how you can use Jenkins:
• Work on the feature or bug fix.

• Test it on your local instance.

• Commit or push the changes to the central repository. For example, SVN or GIT.

• Have Jenkins configured to kick start the build production, and deploy packages to the AEM
Integration Server.

• Move the feature or bug-fix to the Quality Assurance (QA) team.

• QA team will pick up the feature or bug-fix, and test code changes on the Integration Server.

NOTE: If the build is not successful, Jenkins can identify the faulty source code that was
checked in to the repository, and then notify the developer of the error. This must be fixed
before the next build process can take place.AEM plug-in for Eclipse

Extend and Customize Adobe Experience Manager 376


Exercise 1: Create unit tests using Mockito
In this lab exercise, you will create a unit test and test StockModel java class you created
previously. This is a basic unit Test outside the server.

1. Launch Eclipse by double-clicking the Eclipse shortcut on the desktop. The Eclipse Launcher
opens.

ADOBE COPYRIGHT PROTECTED


2. Specify the Workspace as C:\Workspace and click Launch. The Eclipse Workspace opens.

3. In Project Explorer, navigate to training.core > src/test/java.

4. Copy the file StockModelMockitoTest.java from /Exercise_Files/14_Writing_Tests/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


/14_Writing_Tests/. Copy and paste the file from the exercise files referenced to
StockModelMockitoTest.java to Eclipse. Please do not copy the code from this exercise
book. The code in the student guide is for illustrative purposes only.

5. In Eclipse, right-click com.adobe.training.core.models and paste the file. The


StockModelMockitoTest.java class is created, as shown:

Extend and Customize Adobe Experience Manager 377


6. Double-click StockModelMockitoTest.java. The file opens in the editor.

1. package com.adobe.training.core.models;
2.
3. import static org.junit.Assert.assertFalse;
4. import static org.junit.Assert.assertNotNull;
5. import static org.junit.Assert.assertTrue;
6. import static org.mockito.Mockito.mock;
7. import static org.mockito.Mockito.when;
8.
9. import java.text.SimpleDateFormat;
10. import java.util.Calendar;
11. import java.util.Date;
12. import java.util.Random;
13.

ADOBE COPYRIGHT PROTECTED


14. import org.apache.sling.api.resource.Resource;
15. import org.junit.Before;
16. import org.junit.Test;
17.
18. /**
19. * Tests the StockModel using the Mockito testing framework
20. * Note that the testable class is under /src/main/java:
21. * com.adobe.training.core.models.StockModel.java
22. *
23. * To correctly use this testing class:
24. * -put this file under training.core/src/test/java in the package com.adobe.training.core.models
25. *
26. */
27. public class StockModelMockitoTest {
28.
29. private StockModel stock;
30.
31. @Before
32. public void setup() throws Exception {
33.
34. //Adapt the Resource if needed
35. Resource RESOURCE_MOCK = mock(Resource.class);
36. StockModel STOCKMODEL_MOCK = mock(StockModel.class);
37. when(RESOURCE_MOCK.adaptTo(StockModel.class)).thenReturn(STOCKMODEL_MOCK);
38.
39. stock = STOCKMODEL_MOCK;
40.
41. //Setup lastTrade Property
42. Random rand = new Random();
43. double n = Math.round(100*(rand.nextInt(150) + 100)+rand.nextDouble())/100; //random value between 100.00 a
nd 150.00
44. when(STOCKMODEL_MOCK.getLastTrade()).thenReturn(n);
45.
46.
47. //Setup requestDate Property
48. int numberOfDays = randBetween(1,365);
49. Date date = new SimpleDateFormat("D").parse(numberOfDays + " " + Calendar.getInstance().get(Calendar.YEAR)
);
50. String tradeDate = new SimpleDateFormat("MM/dd/yyyy").format(date);
51. when(STOCKMODEL_MOCK.getRequestDate()).thenReturn(tradeDate);

Extend and Customize Adobe Experience Manager 378


52.
53. }
54.
55. private static int randBetween(int start, int end) {
56. return start + (int)Math.round(Math.random() * (end - start));
57. }
58.
59. @Test
60. public void testGetLadeTradeValue() throws Exception{
61. assertNotNull("lastTradeModel is null", stock);
62. assertTrue("lastTrade value is incorrect", stock.getLastTrade() > 100);
63. assertFalse("requestDate value is incorrect", stock.getRequestDate().isEmpty());
64. }
65.
66. }

ADOBE COPYRIGHT PROTECTED


7. Examine the code.

NOTE: This class will test the Java class StockModel.java.

8. Right-click StockModelMockitoTest.java and select Run As > Junit Test. The Junit Test starts.

9. Verify the test ran successfully, as shown:

NOTE: Alternatively, you can run all the tests under src/test/java by right-clicking training.core and
selecting Run As > Maven test.

Extend and Customize Adobe Experience Manager 379


Exercise 2: Create unit tests using Sling Mocks
In this lab exercise, you will create unit tests using Sling mocking framework.

This exercise includes three tasks:

1. Observe the sling-mock dependency

2. Create a unit test with sample data

3. Run the test

Task 1: Observe the sling-mock dependency

1. In Project Explorer, navigate to training > pom.xml.

ADOBE COPYRIGHT PROTECTED


2. Double-click pom.xml to open it in editor. The pom.xml opens.

3. Look for the dependency org.apache.sling.testing.sling-mock (around line 578), as shown:

NOTE: This dependency allows you to use the Sling Mocks framework. Sling Mocks allow you to create
mock resources to test our different classes. Mock resources can be autogenerated using JSON files.

4. Navigate to training > core > pom.xml.

5. Double-click pom.xml to open it in editor. The pom.xml opens.

6. Observe the org.apache.sling.testing.sling-mock dependency has been added to training.core


as well (around line 123).

Extend and Customize Adobe Experience Manager 380


Task 2: Observe the sling-mock dependency

1. In Project Explorer, navigate to training.core > src/test/java.

2. Copy the file StockModelSlingMockTest.java from /Exercise_Files/14_Writing_Tests/.

NOTE: The code is provided as part of the Exercise_Files under /Exercise_Files/


/14_Writing_Tests/. Copy and paste the file from the exercise files referenced to
StockModelSlingMockTest to Eclipse. Please do not copy the code from this exercise book. The
code in the student guide is for illustrative purposes only.

3. In Eclipse, right-click com.adobe.training.core.models and paste the file. The

ADOBE COPYRIGHT PROTECTED


StockModelSlingMockTest.java class is created, as shown:Navigate to training > core >
pom.xml.

4. Double-click StockModelSlingMockTest.java. The file opens in editor, as shown:

1. package com.adobe.training.core.models;
2.
3.
4. import static org.junit.Assert.assertEquals;
5. import static org.junit.Assert.assertNotNull;
6.
7. import org.apache.sling.servlethelpers.MockSlingHttpServletRequest;
8.
9. import org.apache.sling.testing.mock.sling.ResourceResolverType;
10. import org.apache.sling.testing.mock.sling.junit.SlingContext;
11.
12. import org.junit.Before;
13. import org.junit.Rule;
14. import org.junit.Test;
15.
16.
17. /**
18. * Tests the StockModelAdaptFromRequest using the Sling Mock implementation for Sling APIs

19. *
20. * A Sling context (the mock environment) needs to be created to test the classes.

Extend and Customize Adobe Experience Manager 381


21. * The implementation used in that example is the ResourceResolver mock,
22. * to allow in-memory reading and writing resource data using the Sling Resource API.
23. *
24. * It also provides support for Sling Models (Sling Models API 1.1 and Impl 1.1 or higher required)

25. *
26. * Note that the testable class is under /src/main/java:
27. * com.adobe.training.core.models.StockModelAdaptFromRequest.java
28. *
29. * To correctly use this testing class:
30. * -
put this file under training.core/src/test/java in the package com.adobe.training.core.models
31. *

ADOBE COPYRIGHT PROTECTED


32. */
33.
34.
35. public class StockModelSlingMockTest {
36.
37. private final double EPSILON = 2.0;
38.
39.
40. @Rule
41. public final SlingContext context = new SlingContext(ResourceResolverType.RESOURCERES
OLVER_MOCK);
42.
43. private StockModelAdaptFromRequest stockModel;
44.
45.
46. @Before
47. public final void setup() throws Exception{
48. // Mock the request object with the class MockSlingHttpServletRequest
49. MockSlingHttpServletRequest request = context.request();
50.
51. // Load the test content from a json file
52. context.load().json("/adbe-content.json", "/content/stocks/ADBE");
53.
54. // Register our model to allow adaptation from the context request
55. context.addModelsForClasses(StockModelAdaptFromRequest.class);
56.
57. // The model accesses the resource data through request suffix
58. context.requestPathInfo().setSuffix("/content/stocks/ADBE");
59.
60. // Adapting the request to our model for testing
61. stockModel = request.adaptTo(StockModelAdaptFromRequest.class);

Extend and Customize Adobe Experience Manager 382


62.
63. }
64.
65.
66. @Test
67. public void testStockModel() {
68.
69. // Validate that the request has been successfully adapted to the model
70. assertNotNull(stockModel);
71. // Test if expected and actual values for lastTrade are off by the value of EPSILON
72. assertEquals(220.0, stockModel.getLastTrade(), EPSILON);
73. }
74.

ADOBE COPYRIGHT PROTECTED


75.
76. }

5. Examine the code. Observe how the mock test loads a json file named /adbe-content.json.

6. In Project Explorer, navigate to training.core > src/test/java.

7. Right-click src/test and select New > Source Folder. The New Source Folder screen opens.

8. Provide the values to create the folder, as shown:

• Folder name: resources

Extend and Customize Adobe Experience Manager 383


9. Click Finish. The folder is created.

10. Copy the file adbe-content.json from /Exercise_Files/14_Writing_Tests/.

11. Right-click resources and select Paste, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 384


Task 3: Run the test

1. Right-click StockModelSlingMockTest.java and select Run As > Junit Test. The test runs.

2. Verify the test ran sucessfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. To run all the tests within the bundle, right-click training.core and select Run As > Junit
Test.

4. Verify the tests ran sucessfully, as shown:

Extend and Customize Adobe Experience Manager 385


Exercise 3: Create unit tests using AEM Mocks
In this lab exercise, you will create unit tests using AEM mocking framework.

This exercise includes three tasks:

1. Observe the AEM-mock dependency

2. Create a unit test with sample data

3. Run the test

Task 1: Observe the AEM-mock dependency

1. In Project Explorer, navigate to training > pom.xml.

ADOBE COPYRIGHT PROTECTED


2. Double-click pom.xml to open it in editor. The pom.xml opens.

3. Look for the dependency io.wcm.testing.aem-mock (around line 585), as shown:

NOTE: This dependency allows you to use the Sling Mocks framework. Sling Mocks allow you to create
mock resources to test our different classes. Mock resources can be autogenerated using JSON files.

4. Navigate to training > core > pom.xml.

5. Double-click pom.xml to open it in editor. The pom.xml opens.

6. Observe the io.wcm.testing.aem-mock dependency has been added to training.core as well


(around line 128).

Extend and Customize Adobe Experience Manager 386


Task 2: Create a unit test with sample data

1. Copy the file PageHelper.java from /Exercise_Files/14_Writing_Tests/.

2. In Project Explorer, navigate to training.core > src/main/java.

3. Right-click com.adobe.training.core and paste the file, as shown:

ADOBE COPYRIGHT PROTECTED


4. Double-click PageHelper.java. The file opens in editor, as shown:

1. package com.adobe.training.core;
2.
3. import org.apache.commons.lang3.StringUtils;
4. import org.slf4j.Logger;
5. import org.slf4j.LoggerFactory;
6.
7. import com.day.cq.wcm.api.Page;
8.
9. public class PageHelper {
10.
11. private static final Logger LOGGER = LoggerFactory.getLogger(PageHelper.class);
12.
13.
14. /**
15. * Returns the page title of the given page. If the page title is empty it will fallback to the title and to the
16. * name of the page.
17. * @param page The page.
18. * @return The best suited title found (or <code>null</code> if page is <code>null</code>).
19. */
20. public static String getPageTitle(final Page page) {
21. if (page != null) {
22. final String title = page.getPageTitle();
23. if (StringUtils.isBlank(title)) {
24. return getTitle(page);
25. }
26. return title;
27. } else {
28. LOGGER.debug("Provided page argument is null");
29. return null;
30. }
31. }
32.
33. /**

Extend and Customize Adobe Experience Manager 387


34. * Returns the title of the given page. If the title is empty it will fallback to the name of the page.
35. * @param page The page.
36. * @return The best suited title found (or <code>null</code> if page is <code>null</code>).
37. */
38. public static String getTitle(final Page page) {
39. if (page != null) {
40. final String title = page.getTitle();
41. if (StringUtils.isBlank(title)) {
42. return page.getName();
43. }
44. return title;
45. } else {
46. LOGGER.debug("Provided page argument is null");
47. return null;
48. }
49. }

ADOBE COPYRIGHT PROTECTED


50.
51. }

NOTE: Now that you have your PageHelper class, you can test it. You will create a AEM Mock class
and the json with our test data.

5. In Project Explorer, navigate to training.core > src/test/java.

6. In Eclipse, right-click src/test/java and select New > class. The New Java class window opens.

7. Provide the values to create the folder, as shown:

• Package: com.adobe.training.core

• Name: PageHelperAEMMockTest

Extend and Customize Adobe Experience Manager 388


ADOBE COPYRIGHT PROTECTED
8. Click Finish. The class gets created, as shown:

9. Copy the content of the file PageHelperAEMMockTest.java from


/Exercise_Files/14_Writing_Tests/

10. In Eclipse, double-click PageHelperAEMMockTest.java in the editor, and replace the content
with the one copied from the exercise file.

11. Save the changes.

Extend and Customize Adobe Experience Manager 389


12. Double-click PageHelperAEMMockTest.java. The file opens in the editor, as shown:

1. package com.adobe.training.core;
2.
3. import static org.junit.Assert.assertEquals;
4.
5. import org.junit.Before;
6. import org.junit.Rule;
7. import org.junit.Test;
8.
9. import com.day.cq.wcm.api.Page;
10.
11. import io.wcm.testing.mock.aem.junit.AemContext;
12.
13. public class PageHelperAEMMockTest {

ADOBE COPYRIGHT PROTECTED


14.
15.
16. /**
17. * Tests the helper methods of the class PageHelper
18. *
19. * An AEM context object (the mock environment) needs to be created to test the methods,
20. * as they require an AEM Page object to be injected.
21. *
22. * AEM Mocks is a mock implementation that extends the Sling Mocks implementation
23. * http://wcm.io/testing/aem-mock/
24. *
25. * Note that the testable class is under /src/main/java:
26. * com.adobe.training.core.PageHelper.java
27. *
28. * To correctly use this testing class:
29. * -put this file under training.core/src/test/java in the package com.adobe.training.core
30. *
31. */
32.
33.
34. @Rule
35. public AemContext context = new AemContext();
36.
37. private Page page;
38.
39. @Before
40. public void setup() throws Exception{
41. // Load the test content from a json file
42. context.load().json("/trainingproject-content.json", "/content/trainingproject");
43.
44. // Adapt the resource defined by the path to an AEM Page
45. page = context.currentPage("/content/trainingproject/en");
46. }
47.
48.
49. @Test
50. public void testGetPageTitle() throws Exception{
51.
52. assertEquals("New Project", PageHelper.getPageTitle(page));
53.

Extend and Customize Adobe Experience Manager 390


54. }
55.
56. @Test
57. public void testGetTitle() throws Exception{
58.
59. assertEquals("English", PageHelper.getTitle(page));
60.
61. }
62.
63. }

13. Examine the code. Observe how the mock test loads a json file named /trainingproject-
content.json.

ADOBE COPYRIGHT PROTECTED


14. Copy the file trainingproject-content.json from /Exercise_Files/14_Writing_Tests/.

15. Right-click resources and select Paste, as shown:

Extend and Customize Adobe Experience Manager 391


Task 3: Run the test

1. Right-click PageHelperAEMMockTest.java and select Run As > Junit Test. The test runs.

2. Verify the test ran sucessfully, as shown:

ADOBE COPYRIGHT PROTECTED


3. To run all the tests within the bundle, right-click training.core and select Run As > Junit
Test.

4. Verify the tests ran successfully, as shown:

Extend and Customize Adobe Experience Manager 392


References and Helpful Links:
Sling Mocks: https://sling.apache.org/documentation/development/sling-mock.html
AEM Mocks: http://wcm.io/testing/aem-mock/usage.html
Maven dependency: https://mvnrepository.com/artifact/io.wcm/io.wcm.testing.aem-mock
Maven dependency: http://wcm.io/testing/aem-mock/usage-content-loader-builder.html

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 393


Appendix A: Creating Project and Workflow

Introduction

The AEM Projects feature helps manage and group all workflows and tasks associated with creating content in
AEM Sites and Assets.

ADOBE COPYRIGHT PROTECTED


Objectives

• Create a project template

• Execute a workflow

• Build and test the workflow

Extend and Customize Adobe Experience Manager 394

21
Exercise 1: Create a project template
When creating custom business processes in AEM, a custom project might be required. In this exercise, you will
create a project template, remove unnecessary tiles from the template, add a custom workflow, and add a new
group called reviewers.

Note: A content package is given in Exercise_Files (training-project-template.zip) with the


completed project template. You can either install this content package or go through the
exercise on your own. If you install the content package, go to step 19, the testing part of this

In CRXDE Lite, navigate to /apps/trainingproject, right-click trainingproject, and click Create >

ADOBE COPYRIGHT PROTECTED


1.
Create node. Enter the following values, as shown:

• Enter projects in the Name field.

• Select sling:Folder from the Type drop-down menu.

2. Click OK. The projects node is created.

3. Click Save All. Your changes are saved.

4. Right-click projects, and click Create > Create node. Enter the following values, as shown:

• Enter templates in the Name field.


• Select sling:Folder from the Type drop-down menu.

Extend and Customize Adobe Experience Manager 395

21
5. Click OK. The node is created.

ADOBE COPYRIGHT PROTECTED


6. Click Save All. Your changes are saved, as shown:

7. Copy /libs/cq/core/content/projects/templates/default to
/apps/trainingproject/projects/templates.

8. Right-click default, and rename to training-project, as shown:

Extend and Customize Adobe Experience Manager 396

21
9. Click Save All. Your changes are saved.

ADOBE COPYRIGHT PROTECTED


10. Edit the following properties of the node training-project with the following values, as shown:

• jcr:title- Enter Training Publishing Project for the Value field.


• jcr:description- Enter Create an ACL Project for the Value field.

11. Click Save All. Your changes are saved.

12. Delete the following under training-project, :

• gadgets/experiences

• gadgets/asset

• Workflows/models/launch

• Wokflow/models/landing

• Workflow/models/email

Extend and Customize Adobe Experience Manager 397

21
13. Click Save All. Your changes are saved.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 398

21
14. Rename workflows/models/sampleworkflow to dynamicediting, as shown:

15. Edit the property, as shown:

ADOBE COPYRIGHT PROTECTED


• modelId- Enter /etc/workflow/models/dynamicediting/jcr:content/model in the Value field.

Note: This workflow named dynamicediting (the value you entered for the modelId in the
current step) does not exist yet, you will create it later.
16. Click Save All. Your changes are saved.

17. Right-click training-project node, and click Create > Create node. The Create Node dialog box
opens.

18. In the Create Node dialog box, enter the following values, as shown:

• Name: roles

• Select nt:unstructured from the Type dropdown.

Extend and Customize Adobe Experience Manager 399

21
19. Click OK. The roles node is created.

ADOBE COPYRIGHT PROTECTED


20. Click Save All. Your changes are saved.

21. Navigate to /libs/cq/core/content/projects/templates/retail-product-photoshoot/roles, and copy


the reviewer node to the /roles node under the training-project node, as shown:

22. Click Save All. Your changes are saved.

23. Navigate to the AEM > Projects. The Projects Console opens, as shown:

Extend and Customize Adobe Experience Manager 400

21
24. Select Create > Project. The template opens.

25. Select Training Publishing Project, and click Next. The Create Project window opens.

ADOBE COPYRIGHT PROTECTED


26. Enter the values for the project, as shown:

o Enter Pages for Publishing for the Title field.

o Enter Carlene Avery in the User field, ensure the dropdown beside it is set to Editors, and click
Add.

o Enter Iris Mccoy in the User field, ensure the dropdown beside it is set to Reviewers, and click
Add.

Extend and Customize Adobe Experience Manager 401

21
27. Click Create in the top right corner. The Success pop-up window opens.

28. Click Open in the pop-up window. Observe the changes of the custom project, as shown:

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 402

21
Exercise 2: Execute a workflow
In this exercise, you will create a launcher that will trigger the workflow. You will manually start a workflow from
the workflow console and implement a process step in an existing workflow.
1. In AEM, navigate to Tools > Workflow > Models, as shown:

ADOBE COPYRIGHT PROTECTED


2. Scroll down to locate the Request for Activation workflow/card.

3. Click the checkmark on the Request for Activation workflow/card. The Request for Activation
workflow/card is selected. A blue highlight appears, as shown.

Extend and Customize Adobe Experience Manager 403

21
4. Click Start Workflow in the top left corner, as shown. The Run Workflow dialog box opens.

ADOBE COPYRIGHT PROTECTED


5. Enter the page location, /content/trainingproject/en in the mandatory Payload field, as shown:

6. Click Run. The workflow process will begin.

7. Click the Inbox at the top right corner. A notification will appear in the Inbox (top right) to
approve page content, as shown:

Extend and Customize Adobe Experience Manager 404

21
ADOBE COPYRIGHT PROTECTED
Note: The page will not be published until it is approved. Recall the process is of two steps:

1. Edit an initial version of the page.

2. Publish the page.

Each of these steps requires approval.

Extend and Customize Adobe Experience Manager 405

21
Exercise 3: Build and test a workflow
In this exercise, you will create a workflow with two tasks - editing and approving. After the content is
approved, it will be published. If it is not approved, it will go back to the editor for a proper feedback loop.
Following are the two tasks in this exercise:
1. Build a workflow

2. Test the workflow

Task 1: Build a workflow

In AEM, navigate to Tools > Workflow > Models, as shown. The Workflow Models window opens.

ADOBE COPYRIGHT PROTECTED


Select Create > Create Model, as shown:

In the Add Workflow Model window, enter the values, as shown:

• Title: Dynamic Editing, Approval, and Publish


• Name: dynamicediting

Extend and Customize Adobe Experience Manager 406

21
ADOBE COPYRIGHT PROTECTED
Click Done. The Dynamic Editing, Approval, and Publish model is created.

In the Workflow Models window, select the checkmark on the model Dynamic Editing, Approval, and
Publish and click Edit, as shown:

Extend and Customize Adobe Experience Manager 407

21
The Workflow Model Dynamic Editing, Approval, and Publish opens for editing, as shown:

ADOBE COPYRIGHT PROTECTED


Delete the Step 1 component by double-clicking it and selecting the Delete icon from the menu bar, as
shown:

Drag the Create Project Task component from the left-hand side into the Drag components here box.

Double-click Project Task Creation Step. The Project Task dialog box opens.

Select the Common tab, and enter the title, shown:

Extend and Customize Adobe Experience Manager 408

21
ADOBE COPYRIGHT PROTECTED
Click the Task tab and enter the following details:

• Name: Edit the Page


• Select the Email checkbox.
• Description: Make the appropriate edits to the page and complete this task.

Select the Advanced Settings tab, and copy and paste the contents from the Editor Script /Exerc-
se_Files/12_Deep_Dive_into_AEM_APIs/workflow-script.txt, as shown:

Click the Done icon (checkmark) on the top of the dialog box to save the changes.

Extend and Customize Adobe Experience Manager 409

21
Drag and drop another Create Project Task component from the left-hand side below Edit the Page in the
model, as shown:

ADOBE COPYRIGHT PROTECTED


Double-click Project Task Creation Step. The Project Task dialog box opens.

Select the Common tab, and enter the title as Approval.

Select the Task tab, and enter the following details:

• Name: Approval Task


• Select the Email checkbox.
• Description: Review the page for approval to publish.

Extend and Customize Adobe Experience Manager 410

21
Select the Routing tab, and add the following actions, as shown:

• Approved
• Denied

ADOBE COPYRIGHT PROTECTED


Select the Advanced Settings tab, and copy and paste the contents of Approver Script from /Exerc-
se_Files/12_Deep_Dive_into_AEM_APIs/workflow-script.txt, as shown:

Click the Done icon (checkmark) on the top of the dialog box to save the changes.

Drag another OR Split component from the left-hand side below Approval in the model, as shown:

Extend and Customize Adobe Experience Manager 411

21
Double-click OR Split. The OR Split Properties dialog box opens.

ADOBE COPYRIGHT PROTECTED


Select the Common tab and enter the title as <<Denied—Approved>> and select the 2 Branches option
button, as shown:

Select the Branch1 tab, and copy and paste the contents of Or Split Branch 1 Scripts from /Exerc-
se_Files/12_Deep_Dive_into_AEM_APIs/workflow-script.txt.

Select the Branch2 tab, and copy and paste the contents of Or Split Branch 2 Script from /Exerc-
se_Files/12_Deep_Dive_into_AEM_APIs/workflow-script.txt, as shown.

Select the Default Route checkbox.

Extend and Customize Adobe Experience Manager 412

21
ADOBE COPYRIGHT PROTECTED
Click the Done icon (checkmark) on the top of the dialog box to save the changes.

In the left branch (Branch 1), drag the Goto Step component and Open the dialog box.

Select the Process tab, and enter the Title, as shown:

Script: Copy and Paste the “GOTO Script” from the workflows-script.txt, as shown:

Select the Common tab, and enter the title, as shown:

Extend and Customize Adobe Experience Manager 413

21
Click the Done icon (checkmark) on the top of the dialog box to save the changes.

ADOBE COPYRIGHT PROTECTED

Extend and Customize Adobe Experience Manager 414

21
In the right branch (Branch 2), drag the Activate Page/Asset component, as shown:

ADOBE COPYRIGHT PROTECTED


At this point the workflow is complete. In the top-right click Sync, as shown. This will write the workflow
model.

Extend and Customize Adobe Experience Manager 415

21
Task 2: Test the workflow

Recall the custom project you created earlier, you referenced the workflow you just created. To view this
connection, go to /apps/trainingproject/projects/templates/training-
project/workflows/models/dynamicediting. To test your workflow, you will open the newly created project
and test the workflow with it.

1. Navigate to AEM > Projects. The Projects Console opens, as shown:

ADOBE COPYRIGHT PROTECTED


2. Click Pages for Publishing Project, and click Next. The Pages for Publishing window opens, as shown:

Extend and Customize Adobe Experience Manager 416

21
3. In the Workflow tile, click Add Work. The Start Workflow window opens, as shown:

ADOBE COPYRIGHT PROTECTED


Note: If your workflow does not appear, double check the workflow added to the project template under
/apps/trainingproject/projects/templates/training-project/workflows/models/dynamicediting.

4. Select Dynamic Editing, Approval, and Publish and click Next, as shown. The Properties window opens.

Extend and Customize Adobe Experience Manager 417

21
5. Enter the details, as shown:
• Title: Edit the Equipment Page

• Assign To: Editors

• Content Path: /content/we-retail/language-masters/en/equipment

ADOBE COPYRIGHT PROTECTED


6. Click Submit in the top right corner. The Workflow is built, as shown:

Note: At this point, you should be able to walk through the edit/approval steps. Even though
the tasks are assigned to different project groups, the admin can see and run through all the
tasks.

Extend and Customize Adobe Experience Manager 418

21
7. Refresh the browser and notice the new Task, as shown:

ADOBE COPYRIGHT PROTECTED


8. Click the ellipsis on the Task Tile. The Inbox opens, as shown:

9. Find the new message in the inbox, as shown:

Extend and Customize Adobe Experience Manager 419

21
10. Select the Edit the Page and click Complete, as shown:

ADOBE COPYRIGHT PROTECTED


11. In the Complete Task pop-up window, enter a comment as shown and click Complete, as shown. The task is
completed.

12. Notice there is an Approval Task, as shown:

13. Revert to the admin user.

Extend and Customize Adobe Experience Manager 420

21
14. Impersonate as Carlene Avary, and click OK, as shown:

ADOBE COPYRIGHT PROTECTED


15. Select the Approval Task, and click Complete. The Complete Task pop-up window appears. Notice there is a
drop-down with Approved and Denied, as shown:

If you select Denied, the workflow will step back and create new task and re-edit the page with the comments
of the reviewer (cavery).
If you select Approved, the workflow will move forward.

Extend and Customize Adobe Experience Manager 421

21
16. Complete the task by selecting Approved and add a comment, as shown:

ADOBE COPYRIGHT PROTECTED


17. Verify the page is published, as shown:

Extend and Customize Adobe Experience Manager 422

21

You might also like