diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java index f4d20caf2..21312bf54 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java @@ -5,10 +5,13 @@ import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock; import lombok.extern.slf4j.Slf4j; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.Properties; import java.util.Set; import java.util.function.Consumer; @@ -35,10 +38,6 @@ protected OpenFeatureAPI() { transactionContextPropagator = new NoOpTransactionContextPropagator(); } - private static class SingletonHolder { - private static final OpenFeatureAPI INSTANCE = new OpenFeatureAPI(); - } - /** * Provisions the {@link OpenFeatureAPI} singleton (if needed) and returns it. * @@ -86,7 +85,7 @@ public Client getClient() { * Multiple clients can be used to segment feature flag configuration. * If there is already a provider bound to this domain, this provider will be used. * Otherwise, the default provider is used until a provider is assigned to that domain. - * + * * @param domain an identifier which logically binds clients with providers * @return a new client instance */ @@ -100,8 +99,8 @@ public Client getClient(String domain) { * Multiple clients can be used to segment feature flag configuration. * If there is already a provider bound to this domain, this provider will be used. * Otherwise, the default provider is used until a provider is assigned to that domain. - * - * @param domain a identifier which logically binds clients with providers + * + * @param domain a identifier which logically binds clients with providers * @param version a version identifier * @return a new client instance */ @@ -111,6 +110,17 @@ public Client getClient(String domain, String version) { version); } + /** + * Gets the global evaluation context, which will be used for all evaluations. + * + * @return evaluation context + */ + public EvaluationContext getEvaluationContext() { + try (AutoCloseableLock __ = lock.readLockAutoCloseable()) { + return this.evaluationContext; + } + } + /** * Sets the global evaluation context, which will be used for all evaluations. * @@ -124,17 +134,6 @@ public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) return this; } - /** - * Gets the global evaluation context, which will be used for all evaluations. - * - * @return evaluation context - */ - public EvaluationContext getEvaluationContext() { - try (AutoCloseableLock __ = lock.readLockAutoCloseable()) { - return this.evaluationContext; - } - } - /** * Return the transaction context propagator. */ @@ -175,39 +174,6 @@ public void setTransactionContext(EvaluationContext evaluationContext) { this.transactionContextPropagator.setTransactionContext(evaluationContext); } - /** - * Set the default provider. - */ - public void setProvider(FeatureProvider provider) { - try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { - providerRepository.setProvider( - provider, - this::attachEventProvider, - this::emitReady, - this::detachEventProvider, - this::emitError, - false); - } - } - - /** - * Add a provider for a domain. - * - * @param domain The domain to bind the provider to. - * @param provider The provider to set. - */ - public void setProvider(String domain, FeatureProvider provider) { - try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { - providerRepository.setProvider(domain, - provider, - this::attachEventProvider, - this::emitReady, - this::detachEventProvider, - this::emitError, - false); - } - } - /** * Set the default provider and wait for initialization to finish. */ @@ -226,8 +192,8 @@ public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError /** * Add a provider for a domain and wait for initialization to finish. * - * @param domain The domain to bind the provider to. - * @param provider The provider to set. + * @param domain The domain to bind the provider to. + * @param provider The provider to set. */ public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError { try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { @@ -286,6 +252,39 @@ public FeatureProvider getProvider(String domain) { return providerRepository.getProvider(domain); } + /** + * Set the default provider. + */ + public void setProvider(FeatureProvider provider) { + try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { + providerRepository.setProvider( + provider, + this::attachEventProvider, + this::emitReady, + this::detachEventProvider, + this::emitError, + false); + } + } + + /** + * Add a provider for a domain. + * + * @param domain The domain to bind the provider to. + * @param provider The provider to set. + */ + public void setProvider(String domain, FeatureProvider provider) { + try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { + providerRepository.setProvider(domain, + provider, + this::attachEventProvider, + this::emitReady, + this::detachEventProvider, + this::emitError, + false); + } + } + /** * Adds hooks for globally, used for all evaluations. * 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) { /** * Fetch the hooks associated to this client. + * * @return A list of {@link Hook}s. */ public List getHooks() { @@ -404,7 +404,7 @@ void addHandler(String domain, ProviderEvent event, Consumer handl /** * Runs the handlers associated with a particular provider. - * + * * @param provider the provider from where this event originated * @param event the event type * @param details the event details @@ -440,4 +440,42 @@ private void runHandlersForProvider(FeatureProvider provider, ProviderEvent even } } } + + private static class SingletonHolder { + private static final OpenFeatureAPI INSTANCE; + + static { + OpenFeatureAPI instance = null; + String cls = System.getenv("OPEN_FEATURE_API_CLASS"); + if (cls == null) { + try (InputStream propertiesFile = + SingletonHolder.class.getResourceAsStream("openfeature.properties")) { + if (propertiesFile != null) { + Properties props = new Properties(); + props.load(propertiesFile); + cls = props.getProperty("openfeature.api.class"); + } + } catch (IOException e) { + log.error("Custom OpenFeatureApi could not be initialized", e); + } + } + if (cls != null) { + try { + Class clazz = Class.forName(cls); + + instance = (OpenFeatureAPI) clazz.newInstance(); + } catch (ClassNotFoundException + | ClassCastException + | InstantiationException + | IllegalAccessException e) { + log.error("Custom OpenFeatureApi could not be initialized", e); + } + } + if (instance == null) { + instance = new OpenFeatureAPI(); + } + + INSTANCE = instance; + } + } }