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.
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"/>
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
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)
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
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.
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
The field is hidden when the flag is disabled
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.
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
No comments:
Post a Comment