package org.togglz.core.activation; import java.util.Locale; import org.togglz.core.Feature; import org.togglz.core.logging.Log; import org.togglz.core.logging.LogFactory; import org.togglz.core.repository.FeatureState; import org.togglz.core.spi.ActivationStrategy; import org.togglz.core.user.FeatureUser; import org.togglz.core.util.Strings; import org.togglz.core.util.Validate; /** * Activation strategy that enables features for a given percentage of users. This strategy is typically used to implement * gradual rollouts. The implementation is based on a hashcode created from the name of the acting user which is calculated by * {@link #calculateHashCode(FeatureUser)}. * * @author Christian Kaltepoth */ public class GradualActivationStrategy implements ActivationStrategy { private final Log log = LogFactory.getLog(GradualActivationStrategy.class); public static final String ID = "gradual"; public static final String PARAM_PERCENTAGE = "percentage"; @Override public String getId() { return ID; } @Override public String getName() { return "Gradual rollout"; } @Override public boolean isActive(FeatureState state, FeatureUser user) { if (user != null && Strings.isNotBlank(user.getName())) { String percentageAsString = state.getParameter(PARAM_PERCENTAGE); try { int percentage = Integer.valueOf(percentageAsString); if (percentage > 0) { int hashCode = Math.abs(calculateHashCode(user, state.getFeature())); return (hashCode % 100) < percentage; } } catch (NumberFormatException e) { log.error("Invalid gradual rollout percentage for feature " + state.getFeature().name() + ": " + percentageAsString); } } return false; } /** * @deprecated Use {@link #calculateHashCode(FeatureUser, Feature)} instead */ @Deprecated protected int calculateHashCode(FeatureUser user) { return calculateHashCode(user, null); } protected int calculateHashCode(FeatureUser user, Feature feature) { Validate.notNull(user, "user is required"); return new StringBuilder() .append(user.getName().toLowerCase(Locale.ENGLISH).trim()) .append(":") .append(feature != null ? feature.name() : "") .toString().hashCode(); } @Override public Parameter[] getParameters() { return new Parameter[] { ParameterBuilder .create(PARAM_PERCENTAGE) .label("Percentage") .matching("\\d{1,3}") .description( "Percentage of users for which the feature should be active (i.e. '25' for every fourth user).") }; } }