Created
April 10, 2018 14:04
-
-
Save efenderbosch/96eb3310b08494911b7c290a14ede2e2 to your computer and use it in GitHub Desktop.
Guice Dynamic Proxy Feature Toggles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Target(PARAMETER) | |
@Retention(RUNTIME) | |
@BindingAnnotation | |
public @interface Disabled {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Target(PARAMETER) | |
@Retention(RUNTIME) | |
@BindingAnnotation | |
public @interface Enabled {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Target(TYPE) | |
@Retention(RUNTIME) | |
public @interface Feature { | |
String key(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() { | |
when(MOCK_FEATURE_FLIP.isFeatureEnabled("test_key")).thenReturn(true); | |
assertThat(SOME_FEATURE.foo(), is("enabled")); | |
} | |
@Test | |
public void test_disabled() { | |
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class FeatureFlipProxyProvider<T> implements Provider<T> { | |
private final T proxy; | |
@SuppressFBWarnings("WEM_WEAK_EXCEPTION_MESSAGING") | |
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