RandomFeature.java 5.92 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.servlet.Filter
 *  javax.servlet.FilterChain
 *  javax.servlet.FilterConfig
 *  javax.servlet.ServletException
 *  javax.servlet.ServletRequest
 *  javax.servlet.ServletResponse
 *  javax.servlet.http.Cookie
 *  javax.servlet.http.HttpServletRequest
 *  javax.servlet.http.HttpServletResponse
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.ConfigurationPolicy
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.apache.sling.featureflags.ExecutionContext
 *  org.apache.sling.featureflags.Feature
 */
package com.adobe.granite.frags.impl;

import com.adobe.granite.frags.impl.AbstractFeature;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.featureflags.ExecutionContext;
import org.apache.sling.featureflags.Feature;

@Service(value={Feature.class, Filter.class})
@Properties(value={@Property(name="service.ranking", intValue={200}), @Property(name="feature.name", label="Name", value={"com.adobe.granite.frags.RandomFeature"}), @Property(name="feature.description", label="Description", value={"Displays the feature only for a small percentage of users"})})
@Component(label="Adobe Granite Random Feature Flag", description="Displays the feature only for a small percentage of users", configurationFactory=1, policy=ConfigurationPolicy.REQUIRE, metatype=1)
public final class RandomFeature
extends AbstractFeature
implements Filter {
    @Property(label="Active percentage", description="How often this flag returns true, randomly, in percent")
    public static String PROP_ACTIVE_PERCENTAGE = "active.percentage";
    @Property(label="Cookie name", description="The Cookie name where stored RandomFeature informations", value={"Granite-Random-Feature"})
    public static String PROP_COOKIE_NAME = "cookie.name";
    @Property(label="Cookie max age", description="The Cookie max age", intValue={Integer.MAX_VALUE})
    public static String PROP_COOKIE_MAX_AGE = "cookie.maxAge";
    static final String FEATURE_NAME = "com.adobe.granite.frags.RandomFeature";
    static final String LABEL = "Adobe Granite Random Feature Flag";
    static final String DESCRIPTION = "Displays the feature only for a small percentage of users";
    static final String DEFAULT_COOKIE_NAME = "Granite-Random-Feature";
    static final int DEFAULT_MAX_AGE = Integer.MAX_VALUE;
    private String cookieName;
    private int cookieMaxAge;
    private double activeRatio;

    @Override
    protected void configure(Map<String, Object> config) {
        this.cookieName = PropertiesUtil.toString((Object)config.get(PROP_COOKIE_NAME), (String)"Granite-Random-Feature");
        this.cookieMaxAge = PropertiesUtil.toInteger((Object)config.get(PROP_COOKIE_MAX_AGE), (int)Integer.MAX_VALUE);
        this.activeRatio = PropertiesUtil.toDouble((Object)config.get(PROP_ACTIVE_PERCENTAGE), (double)50.0) / 100.0;
    }

    public boolean isEnabled(ExecutionContext context) {
        HttpServletRequest request = context.getRequest();
        double assignedRandom = this.getAssignedRandom(request);
        return assignedRandom <= this.activeRatio;
    }

    private double getAssignedRandom(HttpServletRequest request) {
        if (request != null) {
            String cookieValue = null;
            Cookie[] cookies = request.getCookies();
            for (int i = 0; i < cookies.length && cookieValue != null; ++i) {
                Cookie cookie = cookies[i];
                if (!this.cookieName.equals(cookie.getName())) continue;
                cookieValue = cookie.getValue();
            }
            if (cookieValue == null) {
                cookieValue = (String)request.getAttribute(this.cookieName);
            }
            if (cookieValue != null && !cookieValue.isEmpty()) {
                return Double.valueOf(cookieValue);
            }
        }
        return Double.MAX_VALUE;
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        Cookie[] cookies;
        HttpServletRequest servletRequest;
        if (request instanceof HttpServletRequest && !RandomFeature.contains(this.cookieName, cookies = (servletRequest = (HttpServletRequest)request).getCookies())) {
            String random = String.valueOf(Math.random());
            Cookie randomCookie = new Cookie(this.cookieName, random);
            randomCookie.setMaxAge(this.cookieMaxAge);
            HttpServletResponse servletResponse = (HttpServletResponse)response;
            servletResponse.addCookie(randomCookie);
            servletRequest.setAttribute(this.cookieName, (Object)random);
        }
        chain.doFilter(request, response);
    }

    private static boolean contains(String cookieName, Cookie[] cookies) {
        for (Cookie cookie : cookies) {
            if (!cookieName.equals(cookie.getName())) continue;
            return true;
        }
        return false;
    }

    public void destroy() {
    }

    public String toString() {
        return Object.super.toString() + ", active percentage=" + this.activeRatio * 100.0;
    }
}