Skip to content

Instantly share code, notes, and snippets.

@efenderbosch
Created October 5, 2017 19:56
Show Gist options
  • Save efenderbosch/838257bbc3cbfd6926efef0f980f709e to your computer and use it in GitHub Desktop.
Save efenderbosch/838257bbc3cbfd6926efef0f980f709e to your computer and use it in GitHub Desktop.
Feature Toggles w/ LaunchDarkly and Guice
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(PARAMETER)
@Retention(RUNTIME)
@BindingAnnotation
public @interface Disabled {}
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(PARAMETER)
@Retention(RUNTIME)
@BindingAnnotation
public @interface Enabled {}
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(TYPE)
@Retention(RUNTIME)
public @interface Feature {
String key();
}
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.junit.Test;
import javax.inject.Inject;
import static com.google.inject.Scopes.SINGLETON;
import static com.unific.aws.config.featureflag.FeatureFlipAnnotationTest.FeatureFlipTestModule.MOCK_FEATURE_FLIP;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class FeatureFlipAnnotationTest {
private static final Injector INJECTOR = Guice.createInjector(new FeatureFlipTestModule());
private static final SomeFeature SOME_FEATURE = INJECTOR.getInstance(SomeFeature.class);
@Test
public void test_enabled() throws Exception {
when(MOCK_FEATURE_FLIP.isFeatureEnabled("test_key")).thenReturn(true);
assertThat(SOME_FEATURE.foo(), is("enabled"));
}
@Test
public void test_disabled() throws Exception {
when(MOCK_FEATURE_FLIP.isFeatureEnabled("test_key")).thenReturn(false);
assertThat(SOME_FEATURE.foo(), is("disabled"));
}
@Feature(key = "test_key")
public interface SomeFeature {
String foo();
}
public static class EnabledImplementation implements SomeFeature {
@Override
public String foo() {
return "enabled";
}
}
public static class DisabledImplementation implements SomeFeature {
@Override
public String foo() {
return "disabled";
}
}
public static class SomeFeatureProvider extends FeatureFlipProxyProvider<SomeFeature> {
@Inject
public SomeFeatureProvider(FeatureFlip featureFlip,
@Enabled SomeFeature enabledImplementation,
@Disabled SomeFeature disabledImplementation) {
super(SomeFeature.class, featureFlip, enabledImplementation, disabledImplementation);
}
}
public static class FeatureFlipTestModule extends AbstractModule {
public static final FeatureFlip MOCK_FEATURE_FLIP = mock(FeatureFlip.class);
@Override
protected void configure() {
bind(FeatureFlip.class).toInstance(MOCK_FEATURE_FLIP);
bind(SomeFeature.class).annotatedWith(Enabled.class).to(EnabledImplementation.class).in(SINGLETON);
bind(SomeFeature.class).annotatedWith(Disabled.class).to(DisabledImplementation.class).in(SINGLETON);
bind(SomeFeature.class).toProvider(SomeFeatureProvider.class);
}
}
}
import java.lang.reflect.Proxy;
import javax.inject.Provider;
public abstract class FeatureFlipProxyProvider<T> implements Provider<T> {
private final T proxy;
protected FeatureFlipProxyProvider(Class<T> type,
FeatureFlip featureFlip,
T enabledImplementation,
T disabledImplementation) {
Feature feature = type.getAnnotation(Feature.class);
if (feature == null) {
throw new IllegalArgumentException(type.getName() + " must be annotated with @Feature");
}
String key = feature.key();
ClassLoader classLoader = FeatureFlipProxyProvider.class.getClassLoader();
Class<?>[] interfaces = {type};
proxy = (T) Proxy.newProxyInstance(classLoader, interfaces,
(proxy1, method, args) -> featureFlip.isFeatureEnabled(key) ?
method.invoke(enabledImplementation, args) :
method.invoke(disabledImplementation, args));
}
@Override
public T get() {
return proxy;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment