Friday, August 7, 2020

Sling Feature Flags for continuous integration in Adobe Experience Manager(AEM)| Feature Toggles in AEM

Sling Feature Flags for continuous integration in Adobe Experience Manager(AEM)| Feature Toggles in AEM


This tutorial explains the details of Sling Feature Flags in AEM.

Feature Flags

Feature Flags are used to select whether a particular feature is enabled or not. This allows us to continuously deploy new features of an application without making them globally available yet.

apache-sling-feature-flags

Feature flag support consists of two parts:

The feature flag itself represented by the Feature interface and the application providing a feature guarded by a feature flag.

A feature flag is simply a boolean condition that modifies the behavior of a component, module, or function in your application.

Features may be enabled based on various contextual data:

  • Time of Day
  • Segmentation Data (gender, age, etc.), if available
  • Request Parameter
  • Request Header
  • Cookie Value
  • Static Configuration

Configure Feature Flags

Feature flags can be provided by registering org.apache.sling.featureflags.Feature services. Alternatively feature flags can be provided by factory configuration with factory PID org.apache.sling.featureflags.impl.ConfiguredFeature

Enable through Feature service

Enable a Feature service as shown below, the feature service should implement org.apache.sling.featureflags.Feature. Add the logic to enable/disable the feature into the isEnabled(ExecutionContext ec) method, the method returns boolean true(feature enabled) or false(feature disabled) based on the implementation logic. The ExecutionContext interface provides access to the context for evaluating whether a feature is enabled or not. Enable a name and description for the feature, this will help us to identify the features through feature console.

import org.apache.sling.featureflags.Feature;@Component(service = Feature.class, immediate = true)
public class SampleFeature implements Feature {
@Override
public String getDescription() {
return "Sample Feature";
}
@Override
public String getName() {
return "sample-feature";
}
@Override
public boolean isEnabled(ExecutionContext ec) {

// Write a logic to evaluate the feature flag to true or false

return false;

}

}

This is a dynamic configuration, the logic is evaluated to identify the flag value.

As an example I am creating a feature — CalculateTotal, based on the request parameter(newTotal=true/false) the corresponding calculation logic is enabled, implement the required logic to enable or disable the features.

import org.apache.sling.featureflags.ExecutionContext;
import org.apache.sling.featureflags.Feature;
import org.osgi.service.component.annotations.Component;
@Component(service = Feature.class, immediate = true)
public class CalculateNewTotalFeature implements Feature {

@Override
public String getDescription() {
return "Calculate New Total Feature";
}
@Override
public String getName() {
return "CalculateNewTotalFeature";
}
@Override
public boolean isEnabled(ExecutionContext ec) {
// Feature Flag to enable new total calculation based on the request parameter
if(ec.getRequest()!=null)
{
return Boolean.valueOf(ec.getRequest().getParameter("newTotal"));
}

return false;
}
}

Sample Servlet guarded by the feature flag

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.featureflags.Features;
import org.osgi.framework.Constants;
import org.apache.sling.api.servlets.HttpConstants;
@Component(service=Servlet.class,immediate = true,
property={
Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
"sling.servlet.methods=" + HttpConstants.METHOD_GET,
"sling.servlet.paths="+ "/services/calculateTotalServlet",
})
public class CalculateTotalServlet extends SlingSafeMethodsServlet {

@Reference
private Features features;
private static final long serialVersionUID = 1L;@Override
protected void doGet(final SlingHttpServletRequest req,
final SlingHttpServletResponse resp) throws ServletException, IOException {
int total=0;
if(features.isEnabled("CalculateNewTotalFeature"))
{
total=Integer.valueOf(req.getParameter("qty"))*Integer.valueOf(req.getParameter("val"))*Integer.valueOf(req.getParameter("tax"));
}else
{
total=Integer.valueOf(req.getParameter("qty"))*Integer.valueOf(req.getParameter("val"));
}
resp.setContentType("text/plain");
resp.getWriter().write("Total = " +total) ;
}
}

Different calculation logic is used based on the newTotal flag value in the request parameter

http://localhost:4502/services/calculateTotalServlet?qty=2&val=3&tax=10&newTotal=true — new logic is used and the outcome is 60

http://localhost:4502/services/calculateTotalServlet?qty=2&val=3&tax=10&newTotal=false — Old logic is used and the outcome is 6

Enable through factory PID org.apache.sling.featureflags.Feature

The feature can also be enabled through factory PID org.apache.sling.featureflags.impl.ConfiguredFeature (Apache Sling Configured Feature)

Enable the run mode-specific configuration — org.apache.sling.featureflags.impl.ConfiguredFeature-sample-feature.xml

Sample configure details are

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="sling:OsgiConfig"
name="sample-feature"
description="Sample Feature"
enabled="{Boolean}true"/>
apache-sling-feature-flags

name — Short name of this feature. This name is used to refer to this feature when checking for it to be enabled or not. This property is required and defaults to a name derived from the feature’s class name and object identity. It is strongly recommended to define a useful and unique for the feature

description — Description of the feature. The intent is to describe the behavior of the application if this feature would be enabled. It is recommended to define this property. The default value is the value of the name property.

enabled — Boolean flag indicating whether the feature is enabled or not by this configuration

This is going to be a static configuration, the flag value is set in the configuration. The static features flags can be used in the application as same as the dynamic feature flags to guard the features.

The configurations can be created/modified(not recommended) and verified through OSGI Console

Image for post
apache-sling-feature-flags

The registered feature flags(default and custom) status can be viewed from OSGI console — http://localhost:4502/system/console/features(displays both dynamic and static flags)

apache-sling-feature-flags

In my case, the feature flag(sample-feature) is disabled by default as the logic is based on the request parameter, enabled when the request parameter contains “newTotal=true”

The static flag — sample-feature

Image for post

Feature Flags for Render Condition in Granite UI Fields

Render condition is a mechanic to indicate if the component should be rendered or not. A typical example would be to render a submit button based on whether the current user has a privilege to create or modify a resource.

The actual decision making logic itself is pluggable, where a rendercondition component can be created as needed.

/libs/granite/ui/components/coral/foundation/renderconditions/feature — A condition that decides based on Sling’s Feature Flag. The UI fields can be enabled or disabled based on the Feature Flag configuration.

I want to display the “Additional Comments” dialog field based on the feature flag -”sample-feature”, display the filed if the feature flag is enabled, and hide if disabled.

Let us now enable the render condition for the specific dialog field

Create a node of type “nt:unstructured” with the name “granite:rendercondition” under the dialog field

Add the following properties to the “granite:rendercondition” node

“sling:resourceType” — granite/ui/components/coral/foundation/renderconditions/feature

feature- feature name(sample-feature), the feature can be of the type dynamic or static type.

apache-sling-feature-flags-aem

The dialog field “Additional Comments” is displayed based on the status(enabled or disabled) of the “simple-feature” flag.

The field is displayed when the flag is enabled

apache-sling-feature-flags
apache-sling-feature-flags

The field is hidden when the flag is disabled

apache-sling-feature-flags
apache-sling-feature-flags

Feature Flags in Workflow Launcher

The feature flags can be used to trigger the workflow launchers along with the other conditions.

Features — Add the list of features that must be enabled to trigger the launcher

Disabled Features — Add the list of features that must be disabled to trigger the launcher.

apache-sling-feature-flags

The sling feature flags can be used in AEM to manage(enable/disable) the features externally from the application code, the flags can be dynamic based on the implementation logic or static. The features can be enabled based on different context data e.g request parameters, cookies, resource properties, etc. The feature flags help us to continuously deliver features without impacting the end-users also helps to dry run the new features without enabling to public users.

References

https://sling.apache.org/documentation/the-sling-engine/featureflags.html

https://helpx.adobe.com/experience-manager/6-5/sites/developing/using/reference-materials/granite-ui/api/jcr_root/libs/granite/ui/components/coral/foundation/renderconditions/feature/index.html

https://helpx.adobe.com/experience-manager/6-5/sites/developing/using/reference-materials/javadoc/org/apache/sling/featureflags/package-summary.html



Saturday, July 18, 2020

Configure Sling Mapping for Resource Resolution in Adobe Experience Manager — Deep Dive

This tutorial explains the complete details on configuring sling mapping for resource resolution in Adobe Experience Manager.
Resource mapping is used to define the content mapping and redirects in AEM.
I have enabled two sample domains www.example1.com and www.example2.com pointing to /content/wknd/us/en and /content/wknd/ca/en nodes respectively(sample wknd website nodes).
The flow is as below


sling-resource-mapping-in-adobe-experience-manager

The user request for www.example1.com and www.example2.com, the Apache redirects the user to the domain specific landing pages and also redirect the user to the shortened URL(hide/content/wknd/<country code> if user access the page with full content path). The dispatcher pass through the shortened content URL’s to publisher by appending the full content path /content/wknd/<country code>
As first step let us enable the virtual host configurations for www.example1.com and www.example2.com in Apache(Dispatcher)
“DispatcherUseProcessedURL on” configuration should be enabled in dispatcher configuration to enable the Pass Through URL’s to work
e.g
Now both the domains www.example1.com and www.example2.com are accessible, this will display the mapped landing page content but the internal page links still point to the complete URL(/content/wknd/<<country code>>)


Image for post
sling-resource-mapping-in-adobe-experience-manager

Let us now enable the run mode specific(my case the server is enabled with additional run mode “dev”) ResourceResolver configuration — “Apache Sling Resource Resolver Factory” to reverse map the internal full content path to the shortened URL— enable the mapping for all the required content paths.


Image for post

Now the internal page links are transformed to shortened URL based on the URL mappings in the Resource Resolver.


Image for post
Image for post

This approach has multiple draw backs —
  • the mapping don’t supports the regex expressions
  • it doesn’t support cross-site links because this rewriting method is not domain-aware
  • reverse map the content path to the corresponding domain wont work
The incoming URL will be resolved without any issue as the dispatcher always send the complete path(e.g /content/wknd/us/en.html) to the publisher — http://localhost:4503/system/console/jcrresolver.


sling-resource-mapping-in-adobe-experience-manager
Image for post

The content path is not reversed mapped to the domain URL


Image for post

This will create issues while supporting multiple domains in the AEM platform and need to link the pages between sites(the components should identify the domain corresponding to the content path while linking the pages from different sites — ResourceResolver.map(…))
We should use the etc mapping to enable the forward resolving(resolve the incoming URL to content path) and reverse mapping (map the content path to the domain)
As a first step create an environment specific mapping folder (sling:Folder)— /etc/map.dev.publish, the etc mapping configurations won’t honor the run mode but it is possible to enable the environment specific configuration by enabling different folders e.g map.dev.publish, map.uat.publish, map.stage.publish and map.prod.publish and configure the folder to the run mode specific JCR Resolver configuration( “Apache Sling Resource Resolver Factory”).
Create a folder http or https(sling:Folder) under map.dev.publish based on the protocol used to communicate to publisher, use https even the SSL is terminated at load balancer level — the mapping details can be stored to code repository for better management.


Image for post
sling-resource-mapping-in-adobe-experience-manager
sling-resource-mapping-in-adobe-experience-manager

Enable the below Sling Mappings under /etc/map.dev.publish /http(s) based on the protocol used— this will enable the forward mapping(resolve) — map the incoming URL to the content path(forward mapping is optional as the dispatcher is already sending the complete content path to publisher) and reverse mapping (map)— map the content path to shortened DNS URL.


sling-resource-mapping-in-adobe-experience-manager
Image for post
sling-resource-mapping-in-adobe-experience-manager
Image for post

Now the forward and reverse mapping works as expected


Image for post
sling-resource-mapping-in-adobe-experience-manager
sling-resource-mapping-in-adobe-experience-manager
sling-resource-mapping-in-adobe-experience-manager
Image for post
Image for post

The resolve and map methods of org.apache.sling.api.resource.ResourceResolver interface can be used to resolve/map the resources in java services/components.
Let us now enable some additional sling mappings
Resolve(Forward Mapping) the specific incoming URL to a different content path.
e.g the incoming request to the sitemap.xml file should be internally mapped to a different location where the file is located(under /content/wknd)
Create a node sitemap_mapping of type “sling:Mapping” under /etc/map.dev.publish/www.example1.com and add the below properties


Image for post

Now the request to sitemap.xml — http://www.example.com/sitemap.xml displays the content from /content/wknd/sitemap.xml(enabled sample sitemap.xml file under /content/wknd)


Image for post
sling-resource-mapping-in-adobe-experience-manager

Multiple forward mappings can be created as required for the specific domain.
Reverse Map the URLs to remove the .html extension from the page links
Let us now enable additional reverse mapping to remove the html extension from internal page links.
Create a node reverse_no_html of type “sling:Mapping” under /etc/map.dev.publish/www.example1.com and add the below properties


Image for post
sling-resource-mapping-in-adobe-experience-manager
Image for post

Multiple reverse mappings can be created as required for the specific domain.
Enable external redirects for specific URL’s
Let us now enable a external redirect, redirect the request with string “test” to a external URL — https://albinsblog.com
Create a node external_redirect of type “sling:Mapping” under /etc/map.dev.publish/www.example1.com and add the below properties


sling-resource-mapping-in-adobe-experience-manager

Now the URL http://www.example1.com/test.html will be redirected to https://www.albinsblog.com
The jcr package can be downloaded from the below link

References




The sling mapping helps us to map the incoming request to the internal content path and at the same time map the internal content path to the complete DNS based shortened URL. This will enable the AEM platform to support multi tenants and allows the author to cross link the websites just through the content path(AEM automatically maps the content path to the domain URL)