Feature toggle implementation using Spring boot
A Feature toggle (also known as feature switch, feature flag, feature flipper, conditional feature, etc.) is a technique in software development that is used to hide, enable or disable the feature during run time. Feature toggle implementation is very common agile development practices in the context of continuous deployment and delivery. We can enable or disable these features at application runtime.
Feature toggles are variables that are used inside conditional statements therefore, the logic inside these blocks can be toggled 'on or off' depending on the value of the feature toggles. A block of code which has been toggled 'off' is similar to being commented out. This allows developers to control the flow of their software and bypass features that are not ready for deployment.
Features toggles can be used in the following scenarios.
1. Adding a new feature to an application
2. Enhancing an existing feature in an application
3. Hiding or disabling a feature
4. Extending an interface
Features toggles can be stored as
1. Row entires in a database.
2. A Property in a configuration file.
In this article I am showing one example of implementing feature toggle using spring boot application. Create a spring boot application with gradle and add togglz spring boot starter dependencies. In this example I am using mongo db for storing feature toggles.
Add following dependencies in the build.gradle file
compile("org.togglz:togglz-console:2.4.1.Final")
compile("org.togglz:togglz-spring-security:2.4.1.Final")
compile("com.github.heneke.thymeleaf:thymeleaf-extras-togglz:1.0.1.RELEASE")
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
// https://mvnrepository.com/artifact/org.togglz/togglz-mongodb
compile group: 'org.togglz', name: 'togglz-mongodb', version: '2.4.1.Final'
Define your features in enum like below
public enum MyFeatures implements Feature {
@Label("First Feature")
FEATURE_ONE,
@Label("Second Feature")
FEATURE_TWO;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
ToggleFeature.java
package com.example.demo;
public class ToggleFeature {
private String name;
private boolean active;
public ToggleFeature(){
}
public ToggleFeature(String name, boolean active){
this.name = name;
this.active = active;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
Provide toggle configuration like below
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.togglz.core.repository.StateRepository;
import org.togglz.core.user.NoOpUserProvider;
import org.togglz.core.user.UserProvider;
import org.togglz.mongodb.MongoStateRepository;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
@Configuration
public class ToggleConfig {
private static final String FEATURE_TOGGLE_TABLE = "feature_toggle_table";
@Autowired
private MongoClient mongoClient;
@Bean
public UserProvider userProvider() {
return new NoOpUserProvider();
}
@Bean
public StateRepository getStateRepository() {
StateRepository repository = MongoStateRepository
.newBuilder(mongoClient, "demo")
.collection(FEATURE_TOGGLE_TABLE)
//.authentication("john", "tiger")
.writeConcern(WriteConcern.W1)
.build();
return repository;
}
}
application.properties file
spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 spring.data.mongodb.database=demo
Implemented one REST controller for enabling are disabling features.
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.togglz.core.context.FeatureContext;
import org.togglz.core.manager.FeatureManager;
import org.togglz.core.repository.FeatureState;
import com.example.demo.MyFeatures;
import com.example.demo.ToggleFeature;
@RestController
public class FeatureToggleController {
@Autowired
private FeatureManager manager;
@RequestMapping(value = "/feature/{name}", method = RequestMethod.POST)
public ResponseEntity<ToggleFeature> setFeatureStatus(@PathVariable String name,
@RequestBody ToggleFeature resource) {
FeatureContext.getFeatureManager()
.setFeatureState(new FeatureState(MyFeatures.valueOf(name), resource.isActive()));
return ResponseEntity.ok(new ToggleFeature(name, MyFeatures.valueOf(name).isActive()));
}
@RequestMapping(value = "/featureToggles", method = RequestMethod.GET)
public ResponseEntity<StringBuffer> sayHello() {
StringBuffer sb = new StringBuffer();
if(manager.isActive(MyFeatures.FEATURE_ONE)){
sb.append("FEATURE_ONE is enabled");
}else{
sb.append("FEATURE_ONE is not enabled");
}
if(manager.isActive(MyFeatures.FEATURE_TWO)){
sb.append("\nFEATURE_TWO is enabled");
}else{
sb.append("\nFEATURE_TWO is not enabled");
}
return ResponseEntity.ok(sb);
}
}
After starting spring boot application hit the following url
http://localhost:8080/featureToggles
Currently both features are disabled. Hit the following url to enable FEATURE_ONE toggle and pass the FeatureToggle JSON information as part of body.
http://localhost:8080/feature/FEATURE_ONE
After above operation the feature has been enabled in mongo db.
Now if you hit the following url it will show that FEATURE_ONE toggle is enabled.
Like this we can enable or disable features at runtime.
Thanks,
Keep learning :)
Chief Executive Officer at JVM Indonesia
6yNice sample. But, I think it's better if you save in github repository. It would be helpful