Activation Strategies

Togglz defines the concept of activation strategies. They are responsible for deciding whether an enabled feature is active or not. Activation strategies can, for example, be used to activate features only for specific users, for specific client IPs or at a specified time.

Togglz ships with the following default strategies:

The following sections will describe each strategy in detail. The last section custom strategies describes how to build your own strategies.

Username

Enabling features for specific users was already supported in very early versions of Togglz, even before the activation strategy concept was introduced in Togglz 2.0.0.

If you select this strategy for a feature, you can specify a comma-separated list of users for which the feature should be active. Togglz will use the UserProvider you configured for the FeatureManager to determine the current user and compare it to that list.

Please note that Togglz is case-sensitive when comparing the usernames. So the users admin and Admin are NOT the same.

This strategy is very useful if you want to allow a specific list of users to test a certain feature in production.

Gradual rollout

The gradual rollout strategy allows you to activate a feature to a certain percentage of users. This allows you to test a feature with a very small number of users and increase the number over time until the feature is active for everyone.

If you select this strategy, you will be asked to enter a percentage value. If you for example enter 25, Togglz will activate the feature for every fourth user.

The users are selected by calculating a hash value from the username which is then normalized to a value between 0 and 100. The feature will be active for a user if the hash value is smaller or equal to the percentage you configured. This algorithm ensures that the feature will be active for a user even after you increased the percentage. Have a look at the source code of the strategy for more details.

Release date

The release date strategy can be used to automatically activate a feature at a certain point in time. If you select this strategy, you must enter the date and (optionally) the time when the feature should become active. If you omit the time, the feature will be activated at midnight. The date must be specified in the format 2012-12-31 and the time in the format 14:15:00

Client IP

The client IP strategy allows you to restrict a feature to clients connecting from a specific IP address. The strategy uses HttpServletRequest.getRemoteAddr() to obtain the IP address of the client. The strategy is therefore restricted to web applications running in a Servlet environment.

Please note that the strategy requires you to enter a full IP address. Specifying subnets is currently not supported (patches are welcome). But you can enter multiple IP addresses separated by comma.

Server IP

The Server IP strategy is similar to the Client IP strategy. The only difference is that not the client IP but the server IP address is used. This strategy is very useful for canary testing where you activate a feature only on a subset of your cluster nodes to test for things like performance issues.

The strategy uses NetworkInterface.getNetworkInterfaces() to obtain the IP addresses of the server. All IP addresses returned by this method are compared against the IP addresses configured for the strategy. The feature will be active if there is at least one match.

Script Engine

The ScriptEngine strategy is the most flexible activation strategy. It allows you to use a JSR223 scripting language to implement the test that decides whether the feature is active or not. You can use any language supported by the ScriptEngine of your JVM. The Oracle JVM ships with an ECMAScript engine that allows you to write JavaScript for your check out of the box.

The activation strategy has two parameters. The first one is used to specify the language to use for the script (for example ECMAScript). The second parameters is the script itself. The script must evaluate to a boolean result. The script context is populated with a few variables that may be useful for you:

  • user: The FeatureUser representing the current user.
  • date: The current time as a java.util.Date

This example shows a script written in JavaScript that enables the feature for a user named john on Sundays.

  • Language:
    ECMAScript
  • Script:
    user.name == 'john' && date.day == 0

Please double check your script before using it for a feature. If the script evaluation fails due to some error, the feature will be inactive.

System Properties

The System Properties Activation Strategy allows you to define a strategy that enables or disables a flag based on a system property being set.

The activation strategy has two parameters. The first one is used to specify the property name to use (e.g. togglz.FEATURE_ONE). The second parameter is the value to compare against. If the property value matches the expected value, the feature will be on, otherwise it will be off.

Custom Strategies

The great thing about activation strategies is that it is very easy to add your own strategies. Togglz offers an extension point that allows you to implement a new strategy with only a single class.

All activation strategies have to implement the interface ActivationStrategy. Togglz looks up the implementations using the standard Java ServiceLoader mechanism.

To register a custom activation strategy implementation, you must create a file called META-INF/services/org.togglz.core.spi.ActivationStrategy on your classpath and add single line containing the fully-qualified class name of your implementation class.

The ActivationStrategy interface defines the following methods:

getId()

This method has to return a unique id for the strategy. This id is used to identify the strategy when the feature state is persisted by a StateRepository.

getName()

This method returns the human readable name of the activation strategy. This name will for example be displayed in the Togglz Admin Console.

getParameters()

The getParameters() method can be used by the activation strategy to tell Togglz about the strategy parameters it supports. The method returns an array of Parameter instances. Each element in this array represents a specification for a single parameter.

The simplest way to create Parameter instances is to use ParameterBuilder. The builder offers a fluent API for defining a parameter. However you can also create your own Parameter implementation. This can be useful especially if you need to implement custom validation for a parameter.

isActive()

This method is responsible for the evaluation of the strategy. It is called every time a feature state is checked for a feature which uses the strategy. The method has two arguments, the current user as a FeatureUser and the FeatureState representing the configuration of the feature. The implementation of the method can obtain the values of the strategy parameters by calling FeatureState.getParameter().

Example

Enough theory for now. The easiest way to understand how to build a custom strategy is to see an example.

The following strategy allows you to activate a feature only for specific days of the week. So it can for example be configured so that the feature is only active on Mondays. The strategy defines a single parameter identified by the id day. This value of this parameter is then compared against the current weekday.

public class WeekdayActivationStrategy implements ActivationStrategy {

    private static final String[] SHORT_WEEKDAYS = 
        DateFormatSymbols.getInstance().getShortWeekdays();

    @Override
    public String getId() {
        return "weekday";
    }

    @Override
    public String getName() {
        return "Weekday strategy";
    }

    @Override
    public Parameter[] getParameters() {
        return new Parameter[] {
                ParameterBuilder.create("day").label("Day of week")
        };
    }

    @Override
    public boolean isActive(FeatureState featureState, FeatureUser user) {

        // the weekday name configured for the strategy
        String selectedWeekday = featureState.getParameter("day");

        // get the weekday name for today
        Calendar now = GregorianCalendar.getInstance();
        int currentWeekday = now.get(Calendar.DAY_OF_WEEK);
        String currentWeekdayName = SHORT_WEEKDAYS[currentWeekday];

        // the feature is active if the weekday matches
        return selectedWeekday.equals(currentWeekdayName);

    }

}

Easy, isn't it? :)