Handling Feature Flags in a Java EE Application using Togglz

June 26th, 2013 by

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:

Togglz Configuration Panel

Togglz Configuration Panel

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
Activating a feature

Activating a feature

This is what the administration console looks like with all features enabled .. greeen ….

All features activated

All features activated

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.

Togglz JSF/Facelet Integration

Togglz JSF/Facelet Integration

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:

Feature flag check in a servlet

Feature flag check in a servlet

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/jee-feature-flags.git

Resources

Tags: , , , , , ,

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 93,438 bad guys.

Search
Categories