Skip to content

Commit 0ffb228

Browse files
committed
feat: add mechanism for open-close-principle to OpenFeatureApi
first we check an env var, if this is not set, we use a property file else we fallback furthermore we fallback to the default if the provided class name can not be initialized Signed-off-by: Simon Schrottner <[email protected]>
1 parent 3d0cd62 commit 0ffb228

File tree

1 file changed

+92
-54
lines changed

1 file changed

+92
-54
lines changed

src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock;
66
import lombok.extern.slf4j.Slf4j;
77

8+
import java.io.IOException;
9+
import java.io.InputStream;
810
import java.util.ArrayList;
911
import java.util.Arrays;
1012
import java.util.List;
1113
import java.util.Optional;
14+
import java.util.Properties;
1215
import java.util.Set;
1316
import java.util.function.Consumer;
1417

@@ -35,10 +38,6 @@ protected OpenFeatureAPI() {
3538
transactionContextPropagator = new NoOpTransactionContextPropagator();
3639
}
3740

38-
private static class SingletonHolder {
39-
private static final OpenFeatureAPI INSTANCE = new OpenFeatureAPI();
40-
}
41-
4241
/**
4342
* Provisions the {@link OpenFeatureAPI} singleton (if needed) and returns it.
4443
*
@@ -86,7 +85,7 @@ public Client getClient() {
8685
* Multiple clients can be used to segment feature flag configuration.
8786
* If there is already a provider bound to this domain, this provider will be used.
8887
* Otherwise, the default provider is used until a provider is assigned to that domain.
89-
*
88+
*
9089
* @param domain an identifier which logically binds clients with providers
9190
* @return a new client instance
9291
*/
@@ -100,8 +99,8 @@ public Client getClient(String domain) {
10099
* Multiple clients can be used to segment feature flag configuration.
101100
* If there is already a provider bound to this domain, this provider will be used.
102101
* Otherwise, the default provider is used until a provider is assigned to that domain.
103-
*
104-
* @param domain a identifier which logically binds clients with providers
102+
*
103+
* @param domain a identifier which logically binds clients with providers
105104
* @param version a version identifier
106105
* @return a new client instance
107106
*/
@@ -111,6 +110,17 @@ public Client getClient(String domain, String version) {
111110
version);
112111
}
113112

113+
/**
114+
* Gets the global evaluation context, which will be used for all evaluations.
115+
*
116+
* @return evaluation context
117+
*/
118+
public EvaluationContext getEvaluationContext() {
119+
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
120+
return this.evaluationContext;
121+
}
122+
}
123+
114124
/**
115125
* Sets the global evaluation context, which will be used for all evaluations.
116126
*
@@ -124,17 +134,6 @@ public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext)
124134
return this;
125135
}
126136

127-
/**
128-
* Gets the global evaluation context, which will be used for all evaluations.
129-
*
130-
* @return evaluation context
131-
*/
132-
public EvaluationContext getEvaluationContext() {
133-
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
134-
return this.evaluationContext;
135-
}
136-
}
137-
138137
/**
139138
* Return the transaction context propagator.
140139
*/
@@ -175,39 +174,6 @@ public void setTransactionContext(EvaluationContext evaluationContext) {
175174
this.transactionContextPropagator.setTransactionContext(evaluationContext);
176175
}
177176

178-
/**
179-
* Set the default provider.
180-
*/
181-
public void setProvider(FeatureProvider provider) {
182-
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
183-
providerRepository.setProvider(
184-
provider,
185-
this::attachEventProvider,
186-
this::emitReady,
187-
this::detachEventProvider,
188-
this::emitError,
189-
false);
190-
}
191-
}
192-
193-
/**
194-
* Add a provider for a domain.
195-
*
196-
* @param domain The domain to bind the provider to.
197-
* @param provider The provider to set.
198-
*/
199-
public void setProvider(String domain, FeatureProvider provider) {
200-
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
201-
providerRepository.setProvider(domain,
202-
provider,
203-
this::attachEventProvider,
204-
this::emitReady,
205-
this::detachEventProvider,
206-
this::emitError,
207-
false);
208-
}
209-
}
210-
211177
/**
212178
* Set the default provider and wait for initialization to finish.
213179
*/
@@ -226,8 +192,8 @@ public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError
226192
/**
227193
* Add a provider for a domain and wait for initialization to finish.
228194
*
229-
* @param domain The domain to bind the provider to.
230-
* @param provider The provider to set.
195+
* @param domain The domain to bind the provider to.
196+
* @param provider The provider to set.
231197
*/
232198
public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError {
233199
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
@@ -286,6 +252,39 @@ public FeatureProvider getProvider(String domain) {
286252
return providerRepository.getProvider(domain);
287253
}
288254

255+
/**
256+
* Set the default provider.
257+
*/
258+
public void setProvider(FeatureProvider provider) {
259+
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
260+
providerRepository.setProvider(
261+
provider,
262+
this::attachEventProvider,
263+
this::emitReady,
264+
this::detachEventProvider,
265+
this::emitError,
266+
false);
267+
}
268+
}
269+
270+
/**
271+
* Add a provider for a domain.
272+
*
273+
* @param domain The domain to bind the provider to.
274+
* @param provider The provider to set.
275+
*/
276+
public void setProvider(String domain, FeatureProvider provider) {
277+
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
278+
providerRepository.setProvider(domain,
279+
provider,
280+
this::attachEventProvider,
281+
this::emitReady,
282+
this::detachEventProvider,
283+
this::emitError,
284+
false);
285+
}
286+
}
287+
289288
/**
290289
* Adds hooks for globally, used for all evaluations.
291290
* Hooks are run in the order they're added in the before stage. They are run in reverse order for all other stages.
@@ -300,6 +299,7 @@ public void addHooks(Hook... hooks) {
300299

301300
/**
302301
* Fetch the hooks associated to this client.
302+
*
303303
* @return A list of {@link Hook}s.
304304
*/
305305
public List<Hook> getHooks() {
@@ -404,7 +404,7 @@ void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handl
404404

405405
/**
406406
* Runs the handlers associated with a particular provider.
407-
*
407+
*
408408
* @param provider the provider from where this event originated
409409
* @param event the event type
410410
* @param details the event details
@@ -440,4 +440,42 @@ private void runHandlersForProvider(FeatureProvider provider, ProviderEvent even
440440
}
441441
}
442442
}
443+
444+
private static class SingletonHolder {
445+
private static final OpenFeatureAPI INSTANCE;
446+
447+
static {
448+
OpenFeatureAPI instance = null;
449+
String cls = System.getenv("OPEN_FEATURE_API_CLASS");
450+
if (cls == null) {
451+
try (InputStream propertiesFile =
452+
SingletonHolder.class.getResourceAsStream("openfeature.properties")) {
453+
if (propertiesFile != null) {
454+
Properties props = new Properties();
455+
props.load(propertiesFile);
456+
cls = props.getProperty("openfeature.api.class");
457+
}
458+
} catch (IOException e) {
459+
log.error("Custom OpenFeatureApi could not be initialized", e);
460+
}
461+
}
462+
if (cls != null) {
463+
try {
464+
Class<?> clazz = Class.forName(cls);
465+
466+
instance = (OpenFeatureAPI) clazz.newInstance();
467+
} catch (ClassNotFoundException
468+
| ClassCastException
469+
| InstantiationException
470+
| IllegalAccessException e) {
471+
log.error("Custom OpenFeatureApi could not be initialized", e);
472+
}
473+
}
474+
if (instance == null) {
475+
instance = new OpenFeatureAPI();
476+
}
477+
478+
INSTANCE = instance;
479+
}
480+
}
443481
}

0 commit comments

Comments
 (0)