Using Dynamic Proxies for Feature Toggle - java

We've been using Guice for DI in AWS Lambdas, but now are moving to Spring Boot and long running services.
We've got feature toggles working as dynamic proxies in Guice, but need to implement in Spring.
Say we have a SomeFeature interface and two implementations DisabledImplementation and EnabledImplementation.
I can get really close by tagging DisabledImplementation with #Component("some.feature.disabled") and EnabledImplementation with #Component("some.feature.enabled") and then writing an implementation like this:
#Primary
#Component
public class FlippingFeature implements SomeFeature {
private final SomeFeature enabled;
private final SomeFeature disabled;
private final FeatureFlip featureFlip;
#Inject
public FlippingFeature(#Named("some.feature.enabled") SomeFeature enabled,
#Named("some.feature.disabled") SomeFeature disabled,
FeatureFlip featureFlip) {
this.enabled = enabled;
this.disabled = disabled;
this.featureFlip = featureFlip;
}
#Override
public String foo() {
return featureFlip.isEnabled("some.feature") ? enabled.foo() : disabled.foo();
}
}
But I'd prefer to not write the FlippingFeature class at all and do it w/ a dynamic proxy hidden away. Can I do this with a custom BeanFactoryPostProcessor or something else?

I've got a pretty decent solution now.
#Qualifier
#Retention(RUNTIME)
// tag the disabled feature implementation w/ this annotation
public #interface Disabled {}
#Qualifier
#Retention(RUNTIME)
// tag the enabled feature implementation w/ this annotation
public #interface Enabled {}
#Target(TYPE)
#Retention(RUNTIME)
// tag the feature interface w/ this annotation
public #interface Feature {
String value();
}
// create a concrete implementation of this class for each feature interface and annotate w/ #Primary
// note the use of #Enabled and #Disabled injection qualifiers
public abstract class FeatureProxyFactoryBean<T> implements FactoryBean<T> {
private final Class<T> type;
private FeatureFlag featureFlag;
protected T enabled;
protected T disabled;
protected FeatureProxyFactoryBean(Class<T> type) {
this.type = type;
}
#Autowired
public void setFeatureFlag(FeatureFlag featureFlag) {
this.featureFlag = featureFlag;
}
#Autowired
public void setEnabled(#Enabled T enabled) {
this.enabled = enabled;
}
#Autowired
public void setDisabled(#Disabled T disabled) {
this.disabled = disabled;
}
#Override
public T getObject() {
Feature feature = type.getAnnotation(Feature.class);
if (feature == null) {
throw new IllegalArgumentException(type.getName() + " must be annotated with #Feature");
}
String key = feature.value();
ClassLoader classLoader = FeatureProxyFactoryBean.class.getClassLoader();
Class<?>[] interfaces = {type};
return (T) Proxy.newProxyInstance(classLoader, interfaces,
(proxy1, method, args) -> featureFlag.isEnabled(key) ?
method.invoke(enabled, args) :
method.invoke(disabled, args));
}
#Override
public Class<T> getObjectType() {
return type;
}
}
// test classes
#Feature("test_key")
public interface SomeFeature {
String foo();
}
#Disabled
#Component
public class DisabledFeature implements SomeFeature {
#Override
public String foo() {
return "disabled";
}
}
#Enabled
#Component
public class EnabledFeature implements SomeFeature {
#Override
public String foo() {
return "enabled";
}
}
#Primary
#Component
public class SomeFeatureProxyFactoryBean extends FeatureProxyFactoryBean<SomeFeature> {
public SomeFeatureProxyFactoryBean() {
super(SomeFeature.class);
}
}
Then inject #Inject SomeFeature someFeature where needed and it will get the proxy instance due to the #Primary annotation.
Now we can toggle the feature on and off in Launchdarkly and it (nearly) instantly gets reflected in all running instances without a restart or re-initializing the Spring context.

Related

How to controll the lifecycle of a custom scope in Spring

I have been trying to create a custom scope in Spring Framework, but I fail to understand how is the scope actually instantiated and destroyed.
Assuming I want to create an EventScope and I have a com.example.EventHandler.handleEvent(Event event) method that handles an incoming event.
How do I ensure that every Event starts/stops a scope when it is being handled?
If I have several EventScoped beans wired in, How do I make sure they are using the same scope instance during an event? It seems like a new EventScopeImpl would be created for every bean?
If I have to use a ThreadLocal or some other static context, what is even the point of using a custom scope when I can use the ContextHolder class directly?
My code is as follows?
EventScopeImpl.java
public class EventScopeImpl implements Scope {
private final Map<String, Object> scopedObjects
= Collections.synchronizedMap(new HashMap<String, Object>());
private final Map<String, Runnable> destructionCallbacks
= Collections.synchronizedMap(new HashMap<String, Runnable>());
#Override
public Object get(final String name, final ObjectFactory<?> objectFactory) {
if(!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public Object remove(final String name) {
Optional.ofNullable(destructionCallbacks.remove(name)).ifPresent(Runnable::run);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(final String name, final Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object resolveContextualObject(final String key) {
return EventContextHolder.instance().getValue(key);
}
#Override
public String getConversationId() {
return null;
}
}
EventScope.java
#Qualifier
#Scope(value = "event", proxyMode = ScopedProxyMode.TARGET_CLASS)
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface EventScope{}
EventContextHolder.java
public class EventContextHolder{
#Getter
#Accessors(fluent = true)
public static final EventContextHolder instance = new EventContextHolder();
private final static ThreadLocal<Context> context = ThreadLocal.withInitial(Context::new);
private EventContextHolder(){}
public void setValue(final String key, final Object value){
context.get().data().put(key,value);
}
public Object getValue(final String key){
return context.get().data().get(key);
}
public void clear(){
context.get().data().clear();
}
private static class Context{
#Accessors(fluent = true)
#Getter
private final ConcurrentHashMap<String,Object> data = new ConcurrentHashMap<>();
}
}
EventAspect.java
#Aspect
#Component
public class EventAspect {
#Pointcut("execution(com.example.EventHandler.handleEvent(..))")
public void eventHandlerMethod() {};
#Around("eventHandlerMethod()")
public Object startMessageContext(ProceedingJoinPoint pjp) throws Throwable {
final Event event;
if(pjp.getArgs() > 0 && Event.class.isInstance(pjp.getArgs()[0]){
event = (Event)pjp.getArgs()[0];
}
#Cleanup("clear") EventContextHolder context = EventContextHolder.instance();
context.setValue("event",event);
Object retVal = pjp.proceed();
return retVal;
}
}
EventContext.java
#RequiredArgsConstructor
#Getter
#Component
#EventScope
#Lazy
public class EventContext {
#Lazy
#Value("#{event}")
private Event event;
}
EventScopeConfiguration.java
#Configuration
public class EventScopeConfiguration {
#Bean
public BeanFactoryPostProcessor eventScopeBeanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope(
"event", new EventScopeImpl());
}
}

How to forbid Spring Boot injecting both fields of the same type with the same bean instance?

I have a class with two final fields of the same type, and I need to make second field injected with null if property props.enabled in application.yml is false. However, if it's false Spring Boot injects both fields with the same bean instance.
How to forbid Spring Boot injecting both fields of the same type with the same bean instance?
#AllArgsConstructor
public class MySettings {
private int val;
}
My configuration class
#Configuration
public class MySpringConfig {
#Bean
public MySettings settingsA() {
return new MySettings(1);
}
#Bean
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And this is my class
#Component
#RequiredArgsConstructor
public class MyClass {
private final MySettings settingsA; // MySettings(1)
private final MySettings settingsB; // also MySettings(1) but must be null if props.enabled=false
#Value("${props.enabled}")
private boolean enabled;
...
}
This is part of the real project, so I have very little space to deviate from
UPDATE
I came up with solution of constructor injection but the code starts to look ugly
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
private boolean enabled;
public MyClass(MySettings settingsA,
#Nullable #Qualifier("settingsB") MySettings settingsB,
#Value("${props.enabled}") boolean enabled) {
this.settingsA = settingsA;
this.settingsB = settingsB;
this.enabled = enabled;
}
...
You need a qualifier to tell Spring which bean to inject:
#Configuration
public class MySpringConfig {
#Bean(name = "SettingsA")
public MySettings settingsA() {
return new MySettings(1);
}
#Bean(name = "SettingsB")
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And now in MyClass:
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
public MyClass(#Qualifier("SettingsA") MySettings settingsA, #Qualifier("SettingsB") MySettings settingsB) {
this.settingsA = settingsA;
this.settingsB = settingsB;
}
...
}
However, since one of those Beans might not be available I believe you need to not include it in the constructor injection otherwise you will get an error. In that case you need to do the following:
#Component
public class MyClass {
#Qualifier("SettingsA")
#Autowired
private MySettings settingsA;
#Qualifier("SettingsB")
#Autowired(required = false)
private MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
...
}

Abstract properties in an abstract class [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have an abstract class with some configuration properties value which are set using #Value. I want to reuse the abstract class but with another set of configuration properties. The issue is that, those properties value have already been set in the abstract class and all of the concrete classes have inherited it.
I have thought about:
creating another set of config values in the abstract class, but that seems to be creating duplicate, but this is not quite extensible when in future there is yet a third set of config values.
changing the accessibly of the config value in the abstract class from private to protected and have the concrete class to override it. But I'm not sure this kind of overriding is good as it seems create confusion as to what is the actual config value.
create another abstract class which is similar as "AbstractService" but injecting the different set of config value using #Value. This also seems to create duplication.
public abstract class AbstractService {
#Value("${config1}")
private String config1;
#Value("${config2}")
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class concreteServiceA extends AbstractService {
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class concreteServiceB extends AbstractService {
public void serviceB() {
// using serviceMethod in the abstract class
}
}
Would it be a good way if using constructor to pass the required parameters in the abstract class, and let the concrete classes to use constructor injection and #Value to set those values ? though if there are long list of config values this may not scale well.
public abstract class AbstractService {
private String config1;
private String config2;
public AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
public void serviceMethod() {
//using config1 and config2 values
}
}
public concreteServiceA extends AbstractService {
public concreteServiceA(#Value("${config1}") String config1,
#Value("${config2}") String config2) {
super(config1, config2);
}
public void serviceA() {
//using serviceMethod in the abstract class
}
}
public concreteServiceB extends AbstractService {
public concreteServiceB(#Value("${configB1}") String config1,
#Value("${configB2}") String config2) {
super(config1, config2);
}
public void serviceB() {
//using serviceMethod in the abstract class
}
}
You can go like following:
public abstract class AbstractService {
public void serviceMethod() {
String config1 = getConfig1();
String config2 = getConfig2();
//using config1 and config 2 values
}
public abstract String getConfig1();
public abstract String getConfig2();
}
public class concreteServiceA extends AbstractService {
#Value("${config1}") private String config1;
#Value("${config2}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceA() { // using serviceMethod in the abstract class }
}
public class concreteServiceB extends AbstractService {
#Value("${config1.1}") private String config1;
#Value("${config2.1}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceB() { // using serviceMethod in the abstract class }
}
You could either use setter injection or (probably more elegant) constructor injection like this:
public abstract class AbstractService {
protected AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
private String config1;
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${config1a}") String config1, #Value("${config2a}") String config2) {
super(config1, config2);
}
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class ConcreteServiceB extends AbstractService {
public ConcreteServiceB(#Value("${config1b}") String config1, #Value("${config2b}") String config2) {
super(config1, config2);
}
public void serviceB() {
// using serviceMethod in the abstract class
}
}
But if you have lots of values you can also use setter injection and override the setters in each subclass. Or you can still use constructor injection but pass a container class holding the config like this:
public class ServiceConfig {
private String config1;
private String config2;
// getters, setters and more properties
}
Then pass it like this
public abstract class AbstractService {
private ServiceConfig config;
protected AbstractService(ServiceConfig config) {
this.config = config;
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${configA}") ServiceConfig config) {
super(config);
}
}
You can externalize your properties to specific beans which will be autowired to the concrete classes.
Spring annotation #ConfigurationProperties allows you to initialise simple POJO properties based on properties prefix.
First create your POJO which we will inject in the concrete services :
public class ServiceProperties {
private String config1;
private String config2;
//getters and setters
}
Then create a configuration class in a package scanned by spring :
#Configuration
public class ServicePropertiesConfiguration {
#Bean
#ConfigurationProperties(prefix = "service-a")
public ServiceProperties serviceAProperties() {
return new ServiceProperties();
}
#Bean
#ConfigurationProperties(prefix = "service-b")
public ServiceProperties serviceBProperties() {
return new ServiceProperties();
}
}
As you can see, prefix tells to spring where he has to search the properties. Your application.properties will look like this :
service-a.config1=serviceAConfig1
service-a.config2=serviceAConfig2
service-b.config1=serviceBConfig1
service-b.config2=serviceBConfig2
At this stage, you will have two beans of type ServiceProperties with specific values inside
The abstract service looks like this :
public abstract class AbstractService {
private final ServiceProperties serviceProperties;
protected AbstractService(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void serviceMethod() {
//using config1 and config 2 values
// serviceProperties.getConfig1();
// serviceProperties.getConfig2();
}
}
In the concrete service, you have to use #Qualifier annotation with name of created bean
#Service
public class ConcreteServiceA extends AbstractService{
public ConcreteServiceA(#Qualifier("serviceAProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}
#Service
public class ConcreteServiceB extends AbstractService{
protected ConcreteServiceB(#Qualifier("serviceBProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}

Quarkus does not select bean programatically

I'm trying to select the bean programatically, but quarkus does not injected the bean and throw an exception. It's not supported ?
public enum ReportType {
ONE,
TWO
}
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, PARAMETER, FIELD, TYPE})
#Documented
public #interface Report {
ReportType value();
public static final class Literal extends AnnotationLiteral<Report> implements Report {
private final ReportType value;
public static Literal of(ReportType value) {
return new Literal(value);
}
private Literal(ReportType value) {
this.value = value;
}
public ReportType value() {
return value;
}
}
}
public interface CommonnInterface {
void call();
}
#Report(value = ReportType.ONE)
public class ReportOneBusiness implements CommonnInterface {
#Override
public void call() {
System.out.println("Hello");
}
}
And when we call
CommonnInterface commonnInterface = CDI.current()
.select(
CommonnInterface.class,
Report.Literal.of(ReportType.ONE)
).get();
No bean found for required type [interface org.business.CommonnInterface] and qualifiers [[#org.cdi.Report(value=ONE)]]
You likely need to make the beans unremoveable using the #io.quarkus.arc.Unremovable annotation.
See this for more details.
geoand was right, and I forgot to put #Dependent in the ReportOneBusiness.
The right code for ReportOneBusiness is
#Unremovable
#Dependent
#Report(value = ReportType.ONE)
public class ReportOneBusiness extends CommonnInterface {
#Override
public void call() {
System.out.println("Hello");
}
}

Single provider that creates different types of object based on class binding

I'm trying to inject several datastax Mappers but the Provider creation code is always the same and writing a provider for each type is redundant.
The provider code is
public class FooMapperProvider extends Provider<Mapper<Foo>> () {
private final MappingManager mappingManager
#Inject
FooMapperProvider(MappingManager) {
this.mappingManager = mappingManager;
}
#Override
public Mapper<Foo> get() {
mappingManager.mapper(Foo.class);
}
}
Is it possible to bind or create the provider for
bind(Foo.class).toProvider(GenericMapperProvider.class)
bind(Bar.class).toProvider(GenericMapperProvider.class)
so that get is called in a way mappingManager.mapper can create a mapper based on the class for that specific binding?
I thought about trying something like
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final Class klass;
#Inject
public MapperProvider(MappingManager mappingManager, Class klass) {
this.mappingManager = mappingManager;
this.klass = klass;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(klass);
}
}
but I can't figure out how to specify the class and inject the dependency
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final TypeLiteral<T> type;
#Inject
public MapperProvider(MappingManager mappingManager, TypeLiteral<T> type) {
this.mappingManager = mappingManager;
this.type = type;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(type.getRawType());
}
}
bind(new TypeLiteral<Mapper<Foo>>(){})
.toProvider(new TypeLiteral<MapperProvider<Foo>>(){});

Categories