Skip to content

FF4J does not flip when using AOP annotation @Flip in spring project

I’ve injected ff4j following example. Ff4jConfiguration.class:

@Bean
@ConditionalOnMissingBean
public FF4j getFF4j() {
    return new FF4j("ff4j.xml");
}

and application loader was also changed:

@Import( {..., Ff4jConfiguration.class})
@AutoConfigureAfter(Ff4jConfiguration.class)

my ff4j.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<ff4j xmlns="http://www.ff4j.org/schema/ff4j"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.ff4j.org/schema/ff4j http://ff4j.org/schema/ff4j-1.4.0.xsd">
    <features>
        <feature uid="occurrence-logging" enable="false"/>
        <feature uid="check-no-logging" enable="false"/>
        <feature uid="check-logging" enable="true"/>
    </features>
</ff4j>

My bean to verify ff4j

@Component
public class JustToCheck {
    @Autowired
    private FF4j ff4j;

    @Flip(name="occurrence-logging")
    public void log() {
        System.out.println("hello");
    }

    @Flip(name="check-no-logging")
    public void log2() {
        System.out.println("hello2");
    }

    @Flip(name="check-logging")
    public void log3() {
        System.out.println("hello3");
    }
}

In runtime I see ff4j bean injected correctly with correspond properties:

 ff4j.check("check-no-logging")
 > result=false

 ff4j.check("check-logging")
 > result=true

I expect method log2 will be never called, but it is (All used methods were called, none ignored). Can someone help me what I’ve done wrong here please?

Answer

The annotation Flipis meant to be positionned on an Interface and not on bean. The reason is to enforce people to create different implementations for the same method when using AOP. (simpler when cleaning is required later).

I can propose 2 solutions. The first seems obvious but if you don’t have multiple implementations…

@Component
public class JustToCheck {

    @Autowired
    private FF4j ff4j;
    
    public void log2() {
        if (ff4j.check("check-no-logging")) {
            System.out.println("hello2"); 
        } else {
            System.out.println("As check-no-logging is disable... do nothin");
        }
    }
}

The second is to indeed use AOP and you have to :

  1. Add the Autoproxy located in package org.ff4j.aop in your Spring Context. But it’s done already by adding autoconfigure dependency.

  2. Put @Flipannotation on a interface and create different implementations:

Here is a code sample:

@Component
public interface JustToCheck {
    @Flip(name="check-no-logging", alterBean="just-to-check")
    void log2();
}

@Component("just-to-check")
public class JustToCheckImpl implements JustToCheck {
   public void log2() {
       System.out.println("hello2");
    }
}

@Component("just-to-check-mock")
public class JustToCheckMock implements JustToCheck {

  public void log2() {
    System.out.println("As check-no-logging is disable... do nothing");
  }
}

Bug has been reproduced and the 2 working solutions are available here : https://github.com/clun/ff4j-samples/tree/master/ff4j-sample-sergii