Skip to content

Commit 5b28fd3

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 5b28fd3

File tree

1 file changed

+92
-59
lines changed

1 file changed

+92
-59
lines changed

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

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

8-
import java.util.ArrayList;
9-
import java.util.Arrays;
10-
import java.util.List;
11-
import java.util.Optional;
12-
import java.util.Set;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.util.*;
1311
import java.util.function.Consumer;
1412

1513
/**
@@ -35,10 +33,6 @@ protected OpenFeatureAPI() {
3533
transactionContextPropagator = new NoOpTransactionContextPropagator();
3634
}
3735

38-
private static class SingletonHolder {
39-
private static final OpenFeatureAPI INSTANCE = new OpenFeatureAPI();
40-
}
41-
4236
/**
4337
* Provisions the {@link OpenFeatureAPI} singleton (if needed) and returns it.
4438
*
@@ -86,7 +80,7 @@ public Client getClient() {
8680
* Multiple clients can be used to segment feature flag configuration.
8781
* If there is already a provider bound to this domain, this provider will be used.
8882
* Otherwise, the default provider is used until a provider is assigned to that domain.
89-
*
83+
*
9084
* @param domain an identifier which logically binds clients with providers
9185
* @return a new client instance
9286
*/
@@ -100,8 +94,8 @@ public Client getClient(String domain) {
10094
* Multiple clients can be used to segment feature flag configuration.
10195
* If there is already a provider bound to this domain, this provider will be used.
10296
* 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
97+
*
98+
* @param domain a identifier which logically binds clients with providers
10599
* @param version a version identifier
106100
* @return a new client instance
107101
*/
@@ -111,6 +105,17 @@ public Client getClient(String domain, String version) {
111105
version);
112106
}
113107

108+
/**
109+
* Gets the global evaluation context, which will be used for all evaluations.
110+
*
111+
* @return evaluation context
112+
*/
113+
public EvaluationContext getEvaluationContext() {
114+
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
115+
return this.evaluationContext;
116+
}
117+
}
118+
114119
/**
115120
* Sets the global evaluation context, which will be used for all evaluations.
116121
*
@@ -124,17 +129,6 @@ public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext)
124129
return this;
125130
}
126131

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-
138132
/**
139133
* Return the transaction context propagator.
140134
*/
@@ -175,39 +169,6 @@ public void setTransactionContext(EvaluationContext evaluationContext) {
175169
this.transactionContextPropagator.setTransactionContext(evaluationContext);
176170
}
177171

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-
211172
/**
212173
* Set the default provider and wait for initialization to finish.
213174
*/
@@ -226,8 +187,8 @@ public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError
226187
/**
227188
* Add a provider for a domain and wait for initialization to finish.
228189
*
229-
* @param domain The domain to bind the provider to.
230-
* @param provider The provider to set.
190+
* @param domain The domain to bind the provider to.
191+
* @param provider The provider to set.
231192
*/
232193
public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError {
233194
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
@@ -286,6 +247,39 @@ public FeatureProvider getProvider(String domain) {
286247
return providerRepository.getProvider(domain);
287248
}
288249

250+
/**
251+
* Set the default provider.
252+
*/
253+
public void setProvider(FeatureProvider provider) {
254+
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
255+
providerRepository.setProvider(
256+
provider,
257+
this::attachEventProvider,
258+
this::emitReady,
259+
this::detachEventProvider,
260+
this::emitError,
261+
false);
262+
}
263+
}
264+
265+
/**
266+
* Add a provider for a domain.
267+
*
268+
* @param domain The domain to bind the provider to.
269+
* @param provider The provider to set.
270+
*/
271+
public void setProvider(String domain, FeatureProvider provider) {
272+
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
273+
providerRepository.setProvider(domain,
274+
provider,
275+
this::attachEventProvider,
276+
this::emitReady,
277+
this::detachEventProvider,
278+
this::emitError,
279+
false);
280+
}
281+
}
282+
289283
/**
290284
* Adds hooks for globally, used for all evaluations.
291285
* 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 +294,7 @@ public void addHooks(Hook... hooks) {
300294

301295
/**
302296
* Fetch the hooks associated to this client.
297+
*
303298
* @return A list of {@link Hook}s.
304299
*/
305300
public List<Hook> getHooks() {
@@ -404,7 +399,7 @@ void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handl
404399

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

0 commit comments

Comments
 (0)