Tuesday, December 19, 2023

Togglz Feature Flags for the Java platform

This blog will explains how to use the Togglz library to implement feature flags in Spring Boot applications. Feature flags allow developers to enable or disable features at runtime without redeploying the code. This can be useful for testing new features, rolling out features gradually, or controlling access to features based on user or environment.

Summary
Feature flags are a design pattern that allows developers to enable or disable features at runtime.
Togglz is a library that makes it easy to implement feature flags in Spring Boot applications.
Togglz can be used to control the rollout of new features, test new features, and control access to features based on user or environment.
Togglz can be configured using Java code or an application.yml file.
Togglz can be monitored using the actuator endpoints.
Togglz can be used to deployment base on user,enviroment and time frame.

So what we will be doing in this blog post:-
Introduction to Feature Toggles in Spring Boot Applications

  • Feature toggles are a design pattern that allows enabling or disabling features at runtime.
  • Benefits of using feature toggles include independent control of feature rollouts from deployments.

Simple Use case study :- Shoping site Use Case for Feature Toggles

  • Example scenario: Implementing a bigbang Sale with 5% discount on all products on our site.
  • Traditional approach: Code changes and redeployment needed for each discount change.

Benefits of Runtime Feature Control with Toggles

  • Ability to enable/disable discounts without code changes or redeployment.
  • Feature releases for specific periods or targeted user groups.
  • Faster feedback on new features before production release.

Setting Up Feature Toggles with Togglz Library

  • Demonstration of using the Togglz library to implement runtime feature toggles.
  • Creating a small application with product details and an inventory service.

Applying Discounts with Toggles

  • Defining a private method to apply discounts to product prices.
  • Accessing the product list through an endpoint and verifying prices.

Avoiding Code Changes with Runtime Toggles

  • Highlighting the advantage of avoiding code changes and redeployments for discount adjustments. using postman or admin or external properties files.
  • Using Togglz to enable/disable discounts and verify price changes at runtime.

Now lets create our spring boot application as shown below.

Now lets add belwo dependencies in the pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.togglz</groupId>
            <artifactId>togglz-spring-boot-starter</artifactId>
            <version>3.1.2</version>
        </dependency>
 
        <dependency>
            <groupId>org.togglz</groupId>
            <artifactId>togglz-console</artifactId>
            <version>3.3.3</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

and also add the plugins in build

1
2
3
4
5
6
7
8
<plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>                  
            </configuration>
        </plugin>
    </plugins>

So final pom.xml will be

1- Pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
  </parent>
  <groupId>com.siddhu</groupId>
  <artifactId>togglz-switch-feature</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>togglz-switch-feature</name>
  <description>Togglz: Seamless Feature Rollouts in Spring Boot Apps</description>
  <dependencies>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.togglz</groupId>
            <artifactId>togglz-spring-boot-starter</artifactId>
            <version>3.1.2</version>
        </dependency>
 
        <dependency>
            <groupId>org.togglz</groupId>
            <artifactId>togglz-console</artifactId>
            <version>3.3.3</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>                  
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>       

Here we added acutator so that we can monitor this toggle Behavior through the actuator we need to enable the endpoint in application xml.

Now lets create create our java files that will isplay the product name and amount.

1- SiddhuTogglzApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.siddhu;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.togglz.core.Feature;
import org.togglz.core.manager.FeatureManager;
import org.togglz.core.util.NamedFeature;
 
import com.siddhu.dto.Product;
import com.siddhu.service.InventoryService;
 
@SpringBootApplication
@RestController
public class SiddhuTogglzApplication {
    // autowire FetureManager class of togglz to get the flag values set in application properties files.
    @Autowired
    private FeatureManager manager;
 
    //Create static varible of Feture that fetched the value of TOGGLZ_APPLIED from the property files.
    public static final Feature TOGGLZ_APPLIED = new NamedFeature("TOGGLZ_APPLIED");
 
    //autowire service to get the data.
    @Autowired
    private InventoryService service;
 
 
    @GetMapping("/products")
    public List<Product> showAvailableProducts() {
        //check if TOGGLZ_APPLIED is active in property files.
        if (manager.isActive(TOGGLZ_APPLIED)) {
            return applyTogglz(service.getAllProducts());
        } else {
            return service.getAllProducts();
        }
    }
 
    private List<Product> applyTogglz(List<Product> availableProducts) {
        List<Product> orderListAfterDiscount = new ArrayList<>();
        // Iterating using for loop
        for (int i = 0; i < availableProducts.size(); i++)
            
        {
            Product objProduct = availableProducts.get(i);
            objProduct.setPrice(objProduct.getPrice() - (objProduct.getPrice() * 5 / 100));
            orderListAfterDiscount.add(objProduct);
        }
  
        return orderListAfterDiscount;
    }
 
 
    public static void main(String[] args) {
        SpringApplication.run(SiddhuTogglzApplication.class, args);
    }
 
 
}

2- Product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.siddhu.dto;
 
 
public class Product {
 
    private int id;
    private String name;
    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
    }
    /**
     * @return the id
     */
    public int getId() {
        return id;
    }
    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return the price
     */
    public double getPrice() {
        return price;
    }
    /**
     * @param price the price to set
     */
    public void setPrice(double price) {
        this.price = price;
    }
    private double price;
 
 
}

3- InventoryService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.siddhu.service;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.stereotype.Service;
 
import com.siddhu.dto.Product;
 
@Service
public class InventoryService {
 
    public List<Product> getAllProducts() {
        List<Product> objProductLst = new ArrayList<Product>();
        Product objProduct1 = new Product();
        Product objProduct2 = new Product();
        Product objProduct3 = new Product();
         
        objProduct1.setId(1);
        objProduct1.setName("Product1");
        objProduct1.setPrice(1000);
         
        objProduct2.setId(2);
        objProduct2.setName("Product2");
        objProduct2.setPrice(2000);
         
        objProduct3.setId(3);
        objProduct3.setName("Product3");
        objProduct3.setPrice(3000);
         
         
        objProductLst.add(objProduct1);
        objProductLst.add(objProduct2);
        objProductLst.add(objProduct3);
         
        return objProductLst;
         
    }
}

Let now configure togglz in application.yml as show below.

Since we want to monitor this togglz Behavior through the actuator we need to enable the endpoint okay so go to the application.yml file and Define the management endpoint web exposure I mean you can Define that attribute right management endpoints wave then include which endpoint or what is the end point you want to include it you can simply specify I want to monitor this particular endpoint so I want to enable it by actuator.

5- applicaiton.yml

1
2
3
4
5
6
7
8
9
togglz:
  features:
    TOGGLZ_APPLIED:
      enabled: false 
management:
  endpoints:
    web:
      exposure:
        include: 'togglz'

now let start the application and hit the url http://localhost:8080/products

As you can see you will be able to see the price without discount value change. Now lets change the value of enabled to true in application.yml enabled: true and again hit the url

1
2
3
4
5
6
7
8
9
togglz:
  features:
    TOGGLZ_APPLIED:
      enabled: true 
management:
  endpoints:
    web:
      exposure:
        include: 'togglz'

You can also see the value of the togglz properties in application.yml using below actuator url.

you will see above that now togglz comes into picture and apply 5% discount on all the product.
But here we have manually change the enable flag and also need to restart the application which is not our motive and not desirable in prodcution so for that we have two option

1- using REST call using POSTMAN tool. This rest is provided by Togglx
2- Using Admin screen. This is also provided by the Togglz library.

Lets first try the POSTMAN option and hit the below url with this body

http://localhost:8080/actuator/togglz/TOGGLZ_APPLIED

1
2
3
4
{
"name": "TOGGLZ_APPLIED",
"enabled": false
}

hit the postman post request and now again hit below url

http://localhost:8080/products

you will see discont is disable through postman post call.

Now lets try to use the second option i.e. admin screen and do the same using toggle button. This can be done using belwo url

http://localhost:8080/togglz-console/index

but when you hit this url you will get the belwo screen

this is due to security issues. we are getting the error status code 403 okay so as you know 403 is specific to the security related issue the access forbidden right it means by default this toggle Library enables the security so you have two solution

1- Either we add the security dependence in your code
2- Just disable the security

We will chose to disable the security from application.yml BUT DONT DO THE SAME IN PRODUCTION. This is really risky. In production you should go with the proper security implementation. To disable the security add this in our application.yml and then again hit the url

1
2
3
4
5
  console:
    secured: false
     
     

Now you can play with the butto. You can disable and enable discount as you wish without changing the preperty file and restarting or without using POSTMAN rest call. Lets try it out.

First disable.

Not lets do it enable

Also as we suggested above this togglz library has the ability to perform this operation base on user role, time and Environment like UAT and PROD etc that can be done using setting options as shown below.

Now sometime its better to have a seperate property files i.e.local property files for togglz seperated from our application.yml this can be done by using following tag in our application.yml

features-file: ‘file://C:/to_delete/Togglz/my-togglz-propreties.properties’

and lets add this code in it

TOGGLZ_APPLIED = false

So our final appication.yml will be

1
2
3
4
5
6
7
8
9
10
11
12
togglz:
  features:
    TOGGLZ_APPLIED:
      enabled: true
  features-file: 'file://C:/to_delete/Togglz/my-togglz-propreties.properties'
  console:
    secured: false
management:
  endpoints:
    web:
      exposure:
        include: 'togglz'

now lets restart the application and hit the url

http://localhost:8080/products

As you see even thoug the value of TOGGLZ_APPLIED in application.yml is true but we had applied local properties files where in we had made TOGGLZ_APPLIED as false and hence no discount is applied.

You can download the code from below url.

https://github.com/shdhumale/togglz-switch-feature 

No comments: