Feature toggles (also known as feature flags) are simple. You want to introduce new behavior in a Java application, but you’re not ready to turn it on right away, or you only want to make it available for a subset of users. Maybe another application needs to be ready for the new feature; perhaps the business isn’t prepared to support it yet. So you add a feature toggle to your configuration file or the command line, default it to "off," and move on. I don't blame you if you think there’s something more to it. Sometimes changing configuration is as difficult as pushing new code, and slapping booleans into your code feels superficial. You’re right. There is more. There’s feature flag management. Instead of using conditionals that will inevitably turn into technical debt, you can include feature flags as part of a strategy for improving your code and your ability to support it. This post will tackle feature flag management in Java. We’ll start with a simple flag to control a new feature, and then we'll integrate it into CloudBees Feature Management's secure feature management system.
Basic Feature Toggles for Java
For this tutorial, I’m using a small application called SimpleXKCDClient. You can grab a local copy from here. It uses OkHttp to download JSON information about XKCD comics from the XKCD REST service. As the name implies, it’s simple and gives us a way to focus on feature toggles. We’ll use JUnit tests to illustrate how the toggles change the application behavior. Here’s a Java representation of an XKCD comic:
public class XKCDComic { private String month; private int num; private String year; private String news; private String safe_title; private String transcript; private String alt; private String img; private String title; private String day; private String link; }
Initially, the client retrieves my favorite comic from XKCD:
private String REST_URI = "https://xkcd.com/386/info.0.json"; public XKCDComic getComic() { Request request = new Request.Builder().url(REST_URI).build(); try (Response response = httpClient.newCall(request).execute()) { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(response.body().bytes(), XKCDComic.class); } catch(IOException ioe) { return null; } }
We can verify that we retrieved the correct comic with this unit test:
@Test public void givenRequest_ComicIsDutyCalls() { XKCDComic XKCDComic = simpleXKCDClient.getComic(); assertEquals("Duty Calls", XKCDComic.getTitle()); }
So let’s add a new feature. We want to load a different comic during the holidays.
boolean holidaySeason = true; public XKCDComic getComic() { if (holidaySeason) { REST_URI = "https://xkcd.com/521/info.0.json"; } Request request = new Request.Builder().url(REST_URI).build(); try (Response response = httpClient.newCall(request).execute()) { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(response.body().bytes(), XKCDComic.class); } catch(IOException ioe) { return null; } }
Then we'll add a new test that checks for a different title:
@Test public void givenHolidays_ComicsIsXMasSpecial() { XKCDComic XKCDComic = simpleXKCDClient.getComic(); assertEquals("2008 Christmas Special", XKCDComic.getTitle()); }
We’ve added our first feature flag. If holidaySeason is true, we retrieve a different comic.
An Introduction to Feature Flag Management
Of course, to activate the different behavior, we have to change the code, build, and deploy. That’s not a feature flag. We want to turn features on and off without touching any code. Before fully delving into the management features that CloudBees Feature Management gives us, let’s make another small but still significant change to SimpleXKCDClient.
Getting the Configuration Out of Code
Getting the feature toggle out of code is the first step in managing it. Opening code and changing it is not only unmaintainable; it’s not toggling features. It’s deploying new releases. Let’s create a configuration file:
holidaySeason=false
We’ll call it application.properties. Then let’s override the default constructor to load the configuration file:
public SimpleXKCDClient() throws Exception { InputStream props = new FileInputStream(“src/test/resources/application.properties”); System.getProperties().load(props); holidaySeason = Boolean.parseBoolean(Optional.of(System.getProperty(“holidaySeason”)).orElse(“false”)); }
Now we can edit the properties file to set holidaySeason to true or false and watch the results of our tests change.
Managing Feature Flags Centrally
We’ve done a limited version of feature flag management that requires distributing and modifying configuration files to toggle features Let’s look at a better way.
Getting Started With CloudBees Feature Management
First, you’ll need to create a free CloudBees Feature Management account. Sign in with your account and create a new app:
Be sure to select Java and Java Server, as shown. Next, you’ll see instructions for adding CloudBees Feature Management to your application:
Add the CloudBees Feature Management library to your dependencies. At the time of this writing, the current version of the library is 2.0.1. Here's my Gradle.build:
dependencies { compile group: ‘com.squareup.okhttp3’, name: ‘okhttp’, version: ‘3.10.0’ compile group: ‘io.rollout.rox’, name: ‘rox-java-server’, version: ‘2.0.1’ compile group: ‘com.fasterxml.jackson.core’, name:’jackson-databind‘, version:’2.9.4’ compileonly group: ‘org.projectlombok’, name: ‘lombok’, version: ‘1.16.20’ testCompile group: ‘junit’, name: ‘junit’, version: ‘4.12’ }
Next, add the call to Rox.setup() to initialize the SDK, using your application key.
SimpleXKCDClient() throws Exception { // Initialize ROllout Rox.setup(“replace with your application key”); }
Back on the CloudBees Feature Management website, click Next for the prompt to build and run your application.
Build and run it, and you're rewarded after a few moments.
Implementing a Feature Flag
Now, let’s turn holidaySeason into a managed feature flag. Managed flags are RoxFlags:
public class Flags implements RoxContainer { public RoxFlag holidaySeason = new RoxFlag(); }
They're public members of a RoxContainer. In this example, holidaySeason is created with its default value disabled. RoxFlag will also accept a boolean value as an argument to its constructor as a default value. Next, we can modify our constructor to create the container and register it.
SimpleXKCDClient() throws Exception { // Create Rollout container flags = new Flags(); // Register container with Rollout Rox.register("Flags", flags); // Initialize Rollout Rox.setup("your app key");
Run the application again, and then look for your feature toggle in the CloudBees Feature Management dashboard:
It's there! In our application, we're downloading a document via REST and exiting. Since the feature toggle determines which document we request, we want to update the value of our flag from CloudBees Feature Management before we make the REST request. CloudBees Feature Management's API is asynchronous, so we need to do a little bit of extra work to ensure that we have the correct flag setting before the REST request is initiated. CloudBees Feature Management has more information on how flags are updated here. We can install a configuration fetched handler
that CloudBees Feature Management will call after the configuration is received. By using this callback to set a CountdownLatch, we can block until we're ready. Let's move the CloudBees Feature Management initialization to a private method and install the callback. Then we'll call this new method from our constructor:
private void initializeRox() { CountDownLatch roxFirstFetch = new CountDownLatch(1); try { flags = new Flags(); Rox.register("Flags", flags); RoxOptions options = new RoxOptions.Builder() .withConfigurationFetchedHandler( new ConfigurationFetchedHandler() { @Override public void onConfigurationFetched(FetcherResults arg0) { if (roxFirstFetch.getCount() > 0) { roxFirstFetch.countDown(); System.err.println("Got Rollout configuration"); } } }).build(); Rox.setup("your key", options); roxFirstFetch.await(10, TimeUnit.SECONDS); } catch (InterruptedException ie) { System.err.println("Interrupted waiting for rollout data."); } }
We're ready to start setting our flag from the management console.
Managing a Feature Flag
We manage flags by adding them to experiments. An experiment is a scheme for controlling flags (and other variables) in production. Click on Production in the left-hand side menu, and then click Experiments. This will bring up a screen with a Create Experiment button. Click that, and then fill the new experiment window out appropriately.
Select Set Audience.
And we see a console for setting flags to true, false, or split. If we run our tests now, we see that holidaySeason is false. Let’s change it to true.
When we run our tests again, the results are reversed! We can change the behavior of our application without touching code or configuration files. Before we wrap up, let’s take a look at the experiment on the console again. Flip the flag from true to split.
We don’t just have the ability to change the application behavior from the console; we can also experiment (hence the name) with how often the application loads the different comic. This is the power of feature flags
This Is Just the Beginning
This guide is intended to show you how to get started with CloudBees Feature Management in a Java project. CloudBees Feature Management’s documentation has details on how you can do a great deal more with flags, experiments, groupings, and the management console. You now have an understanding of feature flag management and how it can improve your Java code and help you manage your projects. Get to it!