Feature flags are a common technique, often combined with continuous deployment and delivery and they allow us to rollback a specific feature, to create A/B tests or to rollout a specific feature for a specific test group, a specific amount of users or dedicated systems.
In the following short examples I’d like you to demonstrate how easy it is to implement feature flags with the Togglz framework with a few steps in a Java EE environment.
Project Dependencies
Togglz has a rich set of libraries for different integration points slf4j, cdi, spring, seam, servlet, shiro and others.
The main dependencies from my pom.xml are togglz-core, togglz-console, togglz-jsf and togglz-cdi are used in the following tutorial to add the administration console, facelet and CDI integration:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<togglz.version>2.0.0.RC1</togglz.version>
</properties>
<dependencies>
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-core</artifactId>
<version>${togglz.version}</version>
</dependency>
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-console</artifactId>
<version>${togglz.version}</version>
</dependency>
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-jsf</artifactId>
<version>${togglz.version}</version>
</dependency>
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-cdi</artifactId>
<version>${togglz.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1.2.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
My full pom has some additional settings to integrate the embedded GlassFish Plugin, set the source/target level to Java 7 and add directives for the Maven War Plugin – please feel free to have a look at the source, fork, clone .. see below in the section "Tutorial Sources"..
Creating Feature Flags
A feature flag is implemented as an enum that implements org.togglz.core.Feature.
We may add a description for each feature using @Label, and adding a method isActive allows us to use this enum everywhere in our code.
This makes it easy to remove a feature switch from our code later because we only need to search for references to the specific enum.
package com.hascode.tutorial.fflag.feature;
import org.togglz.core.Feature;
import org.togglz.core.annotation.Label;
import org.togglz.core.context.FeatureContext;
public enum UserFeatures implements Feature {
@Label("Displays basic information for a given user")
DISPLAY_SIMPLE_USER_PROFILE,
@Label("Displays extended information for a given user")
DISPLAY_EXTENDED_USER_PROFILE;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
Feature Flag Configuration
Since we’ve added CDI integration, we’re able to instanciate our feature-flag configuration by creating an @ApplicationScoped class that implements org.togglz.core.manager.TogglzConfig.
Togglz offers different implementations for StateRepository – you may store your state in a database using JDBCStateRepository in a file using FileStateRepository – or for the purpose of a quick tutorial, using an in-memory repository, InMemoryStateRepository.
We’re using the ServletUserProvider here, initialized with the feature-admin role needed to access the administration area for the feature configuration.
package com.hascode.tutorial.fflag.config;
import javax.enterprise.context.ApplicationScoped;
import org.togglz.core.Feature;
import org.togglz.core.manager.TogglzConfig;
import org.togglz.core.repository.StateRepository;
import org.togglz.core.repository.mem.InMemoryStateRepository;
import org.togglz.core.user.UserProvider;
import org.togglz.servlet.user.ServletUserProvider;
import com.hascode.tutorial.fflag.feature.UserFeatures;
@ApplicationScoped
public class FeatureFlagConfiguration implements TogglzConfig {
private static final String featureAdminRole = "feature-admin";
@Override
public Class<? extends Feature> getFeatureClass() {
return UserFeatures.class;
}
@Override
public StateRepository getStateRepository() {
return new InMemoryStateRepository();
}
@Override
public UserProvider getUserProvider() {
return new ServletUserProvider(featureAdminRole);
}
}
If you want to take a quick look without adding authentication and roles to your application, just the following workaround, but please don’t forget to remove it for a production system!
@ApplicationScoped
public class FeatureFlagConfiguration implements TogglzConfig {
@Override
public UserProvider getUserProvider() {
return new UserProvider() {
@Override
public FeatureUser getCurrentUser() {
return new SimpleFeatureUser("admin", true);
}
};
}
}
To make CDI work, we need to add an empty____file named beans.xml in src/main/webapp/WEB-INF!
Managing Feature Flags / Admin Console
When starting the application – in my project you simply need to run mvn to boot an embedded GlassFish with the application deployed – you’re able to load the administration console at the URL /contextPath/togglz/index.
Activation/Deactivation
In my case that means that the console is accessible at http://localhost:8080/togglz-feature-flag-tutorial/togglz/index
The console displays available features and their current state as seen in the following screenshot:
Activation Strategies
The following dialog allows you to activate a feature and chose a specific activation strategy:
-
none: The feature is simply activated
-
Client IP: Activated for clients with a specific IP
-
Gradual rollout: You may select the percentage of users e.g. selecting 25% activates the feature for every fourth user
-
Release date: You may specify the release date in the format yyyy-MM-dd
-
ScriptEngine: You may execute a script language e.g. ECMAScript – I must admit that I’ve never tried this one..
-
Server IP: Restrict the feature to a specific server IP .. e.g. activate for 2 of 6 servers by adding their IP address
-
Username: Restrict to specific users .. you could – for example activate a specific feature for the users test-user and test-admin or something similar
This is what the administration console looks like with all features enabled .. greeen ….
Java Server Faces / Facelet Integration
Now we want to take a look at Togglz’ JSF Integration.. first of all we’re adding configuration for the JSF Servlet and add *.xhtml as the file pattern to activate it.
This is our web.xml in src/main/webapp/WEB-INF:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>
And this is a simple facelet, index.xhtml added to the directory src/main/webapp.
As you can see, we’re able to test if a feature is enabled using #\{features['FEATURE_NAME']} that returns a boolean.
In the following example, I’m displaying/hiding a panelGroup depending an the feature state:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>hasCode.com - Togglz Feature Flag Tutorial</title>
</h:head>
<h:body>
<h1>Features enabled</h1>
<h:panelGroup rendered="#{features['DISPLAY_SIMPLE_USER_PROFILE']}">
Display simple user profile
<hr/>
</h:panelGroup>
<h:panelGroup rendered="#{features['DISPLAY_EXTENDED_USER_PROFILE']}">
Display extended user profile
</h:panelGroup>
</h:body>
</html>
When you now start the application using mvn, the facelet is accessible at http://localhost:8080/togglz-feature-flag-tutorial/index.xhtml.
Programmatic Feature Flag Check
This is the way that we’ll be testing a flag most times – by simply using the enum and the isActive method.
In the following example, I’m adding a feature flag check to a servlet that prints a string depending on the features enabled:
package com.hascode.tutorial.fflag.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hascode.tutorial.fflag.feature.UserFeatures;
@WebServlet(urlPatterns = "/fftest")
public class FeatureFlagTestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(final HttpServletRequest req,
final HttpServletResponse resp) throws ServletException,
IOException {
StringBuilder sb = new StringBuilder();
sb.append("Available features are:\n");
if (UserFeatures.DISPLAY_SIMPLE_USER_PROFILE.isActive()) {
sb.append("\t- Display simple user profiles is enabled\n");
}
if (UserFeatures.DISPLAY_EXTENDED_USER_PROFILE.isActive()) {
sb.append("\t- Display extended user profiles is enabled\n");
}
resp.getWriter().append(sb.toString());
}
}
Finally the servlet output, accessible at http://localhost:8080/togglz-feature-flag-tutorial/fftest should output something like this if both features are enabled:
Tutorial Sources
Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:
git clone https://github.com/hascode/jee-feature-flags.git