Skip to content

Commit 1231328

Browse files
committed
Refactor OpenFeature Java SDK to separate API from implementation
- Created multi-module Maven structure with openfeature-api and openfeature-sdk modules - Moved core interfaces and data types to API module for clean separation - Implemented ServiceLoader pattern for automatic SDK discovery and loading - Created focused interfaces replacing monolithic OpenFeatureAdvanced: * OpenFeatureCore - basic operations * OpenFeatureHooks - hook management * OpenFeatureContext - evaluation context * OpenFeatureEventHandling - provider events * OpenFeatureTransactionContext - transaction context * OpenFeatureLifecycle - shutdown operations - Moved NoOp implementations to internal.noop package for better encapsulation - Created EventProvider interface in API with abstract class in SDK for backward compatibility - Updated HookContext initialization to use builder pattern throughout tests - Migrated tests to appropriate modules (API vs SDK concerns) - Fixed classpath and dependency issues for proper module separation - Updated imports and references to use API interfaces where appropriate 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com> diff --git c/benchmark.txt i/benchmark.txt index e43e684..065a2c5 100644 --- c/benchmark.txt +++ i/benchmark.txt @@ -1,5 +1,5 @@ [INFO] Scanning for projects... -[INFO] +[INFO] [INFO] ------------------------< dev.openfeature:sdk >------------------------- [INFO] Building OpenFeature Java SDK 1.12.1 [INFO] from pom.xml @@ -7,21 +7,21 @@ [WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)' [WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)' [WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)' -[INFO] +[INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ sdk --- [INFO] Deleting /home/todd/git/java-sdk/target -[INFO] +[INFO] [INFO] --- checkstyle:3.5.0:check (validate) @ sdk --- [INFO] Starting audit... Audit done. [INFO] You have 0 Checkstyle violations. -[INFO] +[INFO] [INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk --- [INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec -[INFO] +[INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ sdk --- [INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources -[INFO] +[INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ sdk --- [INFO] Recompiling the module because of changed source code. [INFO] Compiling 65 source files with javac [debug target 1.8] to target/classes @@ -44,24 +44,24 @@ Audit done. [INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java: Recompile with -Xlint:deprecation for details. [INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Some input files use unchecked or unsafe operations. [INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Recompile with -Xlint:unchecked for details. -[INFO] +[INFO] [INFO] --- checkstyle:3.5.0:check (validate) @ sdk --- [INFO] Starting audit... Audit done. [INFO] You have 0 Checkstyle violations. -[INFO] +[INFO] [INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk --- [INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec -[INFO] +[INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ sdk --- [INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources -[INFO] +[INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ sdk --- [INFO] Nothing to compile - all classes are up to date. -[INFO] +[INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk --- [INFO] Copying 2 resources from src/test/resources to target/test-classes -[INFO] +[INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ sdk --- [INFO] Recompiling the module because of changed dependency. [INFO] Compiling 52 source files with javac [debug target 1.8] to target/test-classes @@ -80,29 +80,29 @@ Audit done. [INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/EventsTest.java: Recompile with -Xlint:deprecation for details. [INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Some input files use unchecked or unsafe operations. [INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Recompile with -Xlint:unchecked for details. -[INFO] +[INFO] [INFO] >>> jmh:0.2.2:benchmark (default-cli) > process-test-resources @ sdk >>> -[INFO] +[INFO] [INFO] --- checkstyle:3.5.0:check (validate) @ sdk --- [INFO] Starting audit... Audit done. [INFO] You have 0 Checkstyle violations. -[INFO] +[INFO] [INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk --- [INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec -[INFO] +[INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ sdk --- [INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources -[INFO] +[INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ sdk --- [INFO] Nothing to compile - all classes are up to date. -[INFO] +[INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk --- [INFO] Copying 2 resources from src/test/resources to target/test-classes -[INFO] +[INFO] [INFO] <<< jmh:0.2.2:benchmark (default-cli) < process-test-resources @ sdk <<< -[INFO] -[INFO] +[INFO] +[INFO] [INFO] --- jmh:0.2.2:benchmark (default-cli) @ sdk --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 52 source files to /home/todd/git/java-sdk/target/test-classes @@ -150,7 +150,7 @@ Iteration 1: num #instances #bytes class name (module) 19: 149 1884376 [Ljdk.internal.vm.FillerElement; (java.base@21.0.4) 20: 56476 1807232 java.util.ArrayList$Itr (java.base@21.0.4) 21: 37481 1799088 dev.openfeature.sdk.FlagEvaluationDetails$FlagEvaluationDetailsBuilder - 22: 100001 1600016 dev.openfeature.sdk.NoOpProvider$$Lambda/0x000076e79c02fa78 + 22: 100001 1600016 dev.openfeature.api.NoOpProvider$$Lambda/0x000076e79c02fa78 23: 50000 1600000 [Ldev.openfeature.sdk.EvaluationContext; 24: 50000 1600000 [Ljava.util.List; (java.base@21.0.4) 25: 100000 1600000 dev.openfeature.sdk.OpenFeatureClient$$Lambda/0x000076e79c082800 diff --git c/openfeature-api/pom.xml i/openfeature-api/pom.xml index 3e160ab..a6873a8 100644 --- c/openfeature-api/pom.xml +++ i/openfeature-api/pom.xml @@ -42,7 +42,7 @@ <version>4.8.6</version> <scope>provided</scope> </dependency> - + <!-- Test dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> @@ -77,7 +77,39 @@ </archive> </configuration> </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.13</version> + <executions> + <execution> + <id>jacoco-check</id> + <goals> + <goal>check</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile> + <excludes> + <exclude>dev/openfeature/api/exceptions/**</exclude> + <exclude>dev/openfeature/api/internal/**</exclude> + </excludes> + <rules> + <rule> + <element>PACKAGE</element> + <limits> + <limit> + <counter>LINE</counter> + <value>COVEREDRATIO</value> + <minimum>0.3</minimum> + </limit> + </limits> + </rule> + </rules> + </configuration> + </execution> + </executions> + </plugin> </plugins> </build> -</project> \ No newline at end of file +</project> diff --git c/openfeature-api/src/lombok.config i/openfeature-api/src/lombok.config new file mode 100644 index 0000000..ec3b056 --- /dev/null +++ i/openfeature-api/src/lombok.config @@ -0,0 +1,2 @@ +lombok.addLombokGeneratedAnnotation = true +lombok.extern.findbugs.addSuppressFBWarnings = true diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java i/openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java similarity index 97% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java rename to openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java index 7d5f477..ad2a109 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; /** * A class to help with synchronization by allowing the optional awaiting of the associated action. diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java index 64aae73..39ca965 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java @@ -12,7 +12,7 @@ import java.util.function.Function; public interface EvaluationContext extends Structure { String TARGETING_KEY = "targetingKey"; - + /** * Empty evaluation context for use as a default. */ diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java similarity index 93% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java rename to openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java index f92e24d..0de8e05 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import java.util.HashMap; import java.util.Map; diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java i/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java index 9c9a2f5..7500dbb 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java @@ -11,13 +11,32 @@ import lombok.experimental.SuperBuilder; @Data @SuperBuilder(toBuilder = true) public class EventDetails extends ProviderEventDetails { + /** The domain associated with this event. */ private String domain; + + /** The name of the provider that generated this event. */ private String providerName; - public static EventDetails fromProviderEventDetails(ProviderEventDetails providerEventDetails, String providerName) { + /** + * Create EventDetails from ProviderEventDetails with provider name. + * + * @param providerEventDetails the provider event details + * @param providerName the name of the provider + * @return EventDetails instance + */ + public static EventDetails fromProviderEventDetails( + ProviderEventDetails providerEventDetails, String providerName) { return fromProviderEventDetails(providerEventDetails, providerName, null); } + /** + * Create EventDetails from ProviderEventDetails with provider name and domain. + * + * @param providerEventDetails the provider event details + * @param providerName the name of the provider + * @param domain the domain associated with the event + * @return EventDetails instance + */ public static EventDetails fromProviderEventDetails( ProviderEventDetails providerEventDetails, String providerName, String domain) { return builder() diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java new file mode 100644 index 0000000..e867526 --- /dev/null +++ i/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java @@ -0,0 +1,64 @@ +package dev.openfeature.api; + +/** + * Interface for feature providers that support real-time events. + * Providers can implement this interface to emit events about flag changes, + * provider state changes, and other configuration updates. + * + * @see FeatureProvider + */ +public interface EventProvider extends FeatureProvider { + + /** + * Emit the specified {@link ProviderEvent}. + * + * @param event The event type + * @param details The details of the event + * @return An {@link Awaitable} that can be used to wait for event processing completion + */ + Awaitable emit(ProviderEvent event, ProviderEventDetails details); + + /** + * Emit a {@link ProviderEvent#PROVIDER_READY} event. + * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)} + * + * @param details The details of the event + * @return An {@link Awaitable} that can be used to wait for event processing completion + */ + default Awaitable emitProviderReady(ProviderEventDetails details) { + return emit(ProviderEvent.PROVIDER_READY, details); + } + + /** + * Emit a {@link ProviderEvent#PROVIDER_CONFIGURATION_CHANGED} event. + * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)} + * + * @param details The details of the event + * @return An {@link Awaitable} that can be used to wait for event processing completion + */ + default Awaitable emitProviderConfigurationChanged(ProviderEventDetails details) { + return emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details); + } + + /** + * Emit a {@link ProviderEvent#PROVIDER_STALE} event. + * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)} + * + * @param details The details of the event + * @return An {@link Awaitable} that can be used to wait for event processing completion + */ + default Awaitable emitProviderStale(ProviderEventDetails details) { + return emit(ProviderEvent.PROVIDER_STALE, details); + } + + /** + * Emit a {@link ProviderEvent#PROVIDER_ERROR} event. + * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)} + * + * @param details The details of the event + * @return An {@link Awaitable} that can be used to wait for event processing completion + */ + default Awaitable emitProviderError(ProviderEventDetails details) { + return emit(ProviderEvent.PROVIDER_ERROR, details); + } +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java index 6564a4d..ab86447 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java @@ -6,7 +6,7 @@ import java.util.List; /** * The interface implemented by upstream flag providers to resolve flags for * their service. If you want to support realtime events with your provider, you - * should extend the EventProvider class from the SDK module + * should implement {@link EventProvider} */ public interface FeatureProvider { Metadata getMetadata(); diff --git c/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java deleted file mode 100644 index 48b5176..0000000 --- c/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java +++ /dev/null @@ -1,88 +0,0 @@ -package dev.openfeature.api; - -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -/** - * No-operation implementation of OpenFeatureAPI that provides safe defaults. - * Used as a fallback when no actual implementation is available via ServiceLoader. - * All operations are safe no-ops that won't affect application functionality. - */ -public class NoOpOpenFeatureAPI extends OpenFeatureAPI { - - private static final NoOpClient NO_OP_CLIENT = new NoOpClient(); - - @Override - public Client getClient() { - return NO_OP_CLIENT; - } - - @Override - public Client getClient(String domain) { - return NO_OP_CLIENT; - } - - @Override - public Client getClient(String domain, String version) { - return NO_OP_CLIENT; - } - - @Override - public void setProvider(FeatureProvider provider) { - // No-op - silently ignore - } - - @Override - public void setProvider(String domain, FeatureProvider provider) { - // No-op - silently ignore - } - - @Override - public Metadata getProviderMetadata() { - return () -> "No-op Provider"; - } - - @Override - public Metadata getProviderMetadata(String domain) { - return getProviderMetadata(); - } - - @Override - public void addHooks(Hook... hooks) { - // No-op - silently ignore - } - - @Override - public List<Hook> getHooks() { - return Collections.emptyList(); - } - - @Override - public void clearHooks() { - // No-op - nothing to clear - } - - @Override - public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) { - return this; // No-op - return self for chaining - } - - @Override - public EvaluationContext getEvaluationContext() { - return EvaluationContext.EMPTY; - } - - // Implementation of OpenFeatureEventHandling interface - - @Override - public void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) { - // No-op - silently ignore - } - - @Override - public void removeHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) { - // No-op - silently ignore - } - -} \ No newline at end of file diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java index 872f030..a18028e 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java @@ -1,18 +1,28 @@ package dev.openfeature.api; +import dev.openfeature.api.internal.noop.NoOpOpenFeatureAPI; import java.util.ServiceLoader; -import java.util.function.Consumer; /** * Main abstract class that combines all OpenFeature interfaces. * Uses ServiceLoader pattern to automatically discover and load implementations. * This allows for multiple SDK implementations with priority-based selection. + * + * <p>Implements all OpenFeature interface facets: + * - Core operations (client management, provider configuration) + * - Hook management (global hook configuration) + * - Context management (global evaluation context) + * - Event handling (provider event registration and management) + * - Transaction context (transaction-scoped context propagation) + * - Lifecycle management (cleanup and shutdown) */ -public abstract class OpenFeatureAPI implements - OpenFeatureCore, - OpenFeatureHooks, - OpenFeatureContext, - OpenFeatureEventHandling { +public abstract class OpenFeatureAPI + implements OpenFeatureCore, + OpenFeatureHooks, + OpenFeatureContext, + OpenFeatureEventHandling, + OpenFeatureTransactionContext, + OpenFeatureLifecycle { private static volatile OpenFeatureAPI instance; private static final Object lock = new Object(); @@ -20,7 +30,7 @@ public abstract class OpenFeatureAPI implements /** * Gets the singleton OpenFeature API instance. * Uses ServiceLoader to automatically discover and load the best available implementation. - * + * * @return The singleton instance */ public static OpenFeatureAPI getInstance() { @@ -38,12 +48,11 @@ public abstract class OpenFeatureAPI implements * Load the best available OpenFeature implementation using ServiceLoader. * Implementations are selected based on priority, with higher priorities taking precedence. * If no implementation is available, returns a no-op implementation. - * + * * @return the loaded OpenFeature API implementation */ private static OpenFeatureAPI loadImplementation() { - ServiceLoader<OpenFeatureAPIProvider> loader = - ServiceLoader.load(OpenFeatureAPIProvider.class); + ServiceLoader<OpenFeatureAPIProvider> loader = ServiceLoader.load(OpenFeatureAPIProvider.class); OpenFeatureAPIProvider bestProvider = null; int highestPriority = Integer.MIN_VALUE; @@ -57,8 +66,8 @@ public abstract class OpenFeatureAPI implements } } catch (Exception e) { // Log but continue - don't let one bad provider break everything - System.err.println("Failed to get priority from provider " + - provider.getClass().getName() + ": " + e.getMessage()); + System.err.println("Failed to get priority from provider " + + provider.getClass().getName() + ": " + e.getMessage()); } } @@ -66,8 +75,8 @@ public abstract class OpenFeatureAPI implements try { return bestProvider.createAPI(); } catch (Exception e) { - System.err.println("Failed to create API from provider " + - bestProvider.getClass().getName() + ": " + e.getMessage()); + System.err.println("Failed to create API from provider " + + bestProvider.getClass().getName() + ": " + e.getMessage()); // Fall through to no-op } } @@ -84,7 +93,4 @@ public abstract class OpenFeatureAPI implements instance = null; } } - - - // All methods from the implemented interfaces are abstract and must be implemented by concrete classes -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java index 8246360..99442e7 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java @@ -8,7 +8,7 @@ package dev.openfeature.api; public interface OpenFeatureAPIProvider { /** * Create an OpenFeature API implementation. - * + * * @return the API implementation */ OpenFeatureAPI createAPI(); @@ -16,10 +16,10 @@ public interface OpenFeatureAPIProvider { /** * Priority for this provider. Higher values take precedence. * This allows multiple implementations to coexist with clear precedence rules. - * + * * @return priority value (default: 0) */ default int getPriority() { return 0; } -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java deleted file mode 100644 index cbd7c85..0000000 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java +++ /dev/null @@ -1,68 +0,0 @@ -package dev.openfeature.api; - -import java.util.function.Consumer; - -/** - * Advanced/SDK-specific interface for OpenFeature operations. - * Provides lifecycle management and event handling capabilities. - * Typically only implemented by full SDK implementations. - */ -public interface OpenFeatureAdvanced { - /** - * Shut down and reset the current status of OpenFeature API. - * This call cleans up all active providers and attempts to shut down internal - * event handling mechanisms. - * Once shut down is complete, API is reset and ready to use again. - */ - void shutdown(); - - /** - * Register an event handler for when a provider becomes ready. - * - * @param handler Consumer to handle the event - * @return api instance for method chaining - */ - OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler); - - /** - * Register an event handler for when a provider's configuration changes. - * - * @param handler Consumer to handle the event - * @return api instance for method chaining - */ - OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler); - - /** - * Register an event handler for when a provider becomes stale. - * - * @param handler Consumer to handle the event - * @return api instance for method chaining - */ - OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler); - - /** - * Register an event handler for when a provider encounters an error. - * - * @param handler Consumer to handle the event - * @return api instance for method chaining - */ - OpenFeatureAPI onProviderError(Consumer<EventDetails> handler); - - /** - * Register an event handler for a specific provider event. - * - * @param event the provider event to listen for - * @param handler Consumer to handle the event - * @return api instance for method chaining - */ - OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler); - - /** - * Remove an event handler for a specific provider event. - * - * @param event the provider event to stop listening for - * @param handler the handler to remove - * @return api instance for method chaining - */ - OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler); -} \ No newline at end of file diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java index 3339c8e..9de205b 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java @@ -19,4 +19,4 @@ public interface OpenFeatureContext { * @return evaluation context */ EvaluationContext getEvaluationContext(); -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java index ef4d40e..22254e8 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java @@ -1,5 +1,7 @@ package dev.openfeature.api; +import dev.openfeature.api.exceptions.OpenFeatureError; + /** * Core interface for basic OpenFeature operations. * Provides client management and provider configuration. @@ -42,7 +44,7 @@ public interface OpenFeatureCore { /** * Set the default provider. - * + * * @param provider the provider to set as default */ void setProvider(FeatureProvider provider); @@ -55,6 +57,42 @@ public interface OpenFeatureCore { */ void setProvider(String domain, FeatureProvider provider); + /** + * Sets the default provider and waits for its initialization to complete. + * + * <p>Note: If the provider fails during initialization, an {@link OpenFeatureError} will be thrown. + * It is recommended to wrap this call in a try-catch block to handle potential initialization failures gracefully. + * + * @param provider the {@link FeatureProvider} to set as the default. + * @throws OpenFeatureError if the provider fails during initialization. + */ + void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError; + + /** + * Add a provider for a domain and wait for initialization to finish. + * + * <p>Note: If the provider fails during initialization, an {@link OpenFeatureError} will be thrown. + * It is recommended to wrap this call in a try-catch block to handle potential initialization failures gracefully. + * + * @param domain The domain to bind the provider to. + * @param provider The provider to set. + * @throws OpenFeatureError if the provider fails during initialization. + */ + void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError; + + /** + * Return the default provider. + */ + FeatureProvider getProvider(); + + /** + * Fetch a provider for a domain. If not found, return the default. + * + * @param domain The domain to look for. + * @return A named {@link FeatureProvider} + */ + FeatureProvider getProvider(String domain); + /** * Get metadata about the default provider. * @@ -70,4 +108,4 @@ public interface OpenFeatureCore { * @return the provider metadata */ Metadata getProviderMetadata(String domain); -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java index 336f7d9..20c2f8f 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java @@ -3,30 +3,58 @@ package dev.openfeature.api; import java.util.function.Consumer; /** - * Interface for advanced event handling capabilities. - * This interface provides domain-specific event handler management - * which is typically used by SDK implementations but not required - * for basic API usage. + * Interface for provider event handling operations. + * Provides event registration and management for provider state changes, + * configuration updates, and other provider lifecycle events. */ public interface OpenFeatureEventHandling { - /** - * Add event handlers for domain-specific provider events. - * This method is used by SDK implementations to manage client-level event handlers. - * - * @param domain the domain for which to add the handler - * @param event the provider event to listen for - * @param handler the event handler to add + * Register an event handler for when a provider becomes ready. + * + * @param handler Consumer to handle the event + * @return api instance for method chaining */ - void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler); - + OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler); + /** - * Remove event handlers for domain-specific provider events. - * This method is used by SDK implementations to manage client-level event handlers. - * - * @param domain the domain for which to remove the handler - * @param event the provider event to stop listening for - * @param handler the event handler to remove + * Register an event handler for when a provider's configuration changes. + * + * @param handler Consumer to handle the event + * @return api instance for method chaining */ - void removeHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler); -} \ No newline at end of file + OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler); + + /** + * Register an event handler for when a provider becomes stale. + * + * @param handler Consumer to handle the event + * @return api instance for method chaining + */ + OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler); + + /** + * Register an event handler for when a provider encounters an error. + * + * @param handler Consumer to handle the event + * @return api instance for method chaining + */ + OpenFeatureAPI onProviderError(Consumer<EventDetails> handler); + + /** + * Register an event handler for a specific provider event. + * + * @param event the provider event to listen for + * @param handler Consumer to handle the event + * @return api instance for method chaining + */ + OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler); + + /** + * Remove an event handler for a specific provider event. + * + * @param event the provider event to stop listening for + * @param handler the handler to remove + * @return api instance for method chaining + */ + OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler); +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java index 5888a65..a1fe84b 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java @@ -9,7 +9,7 @@ import java.util.List; public interface OpenFeatureHooks { /** * Adds hooks for globally, used for all evaluations. - * Hooks are run in the order they're added in the before stage. + * Hooks are run in the order they're added in the before stage. * They are run in reverse order for all other stages. * * @param hooks The hooks to add. @@ -27,4 +27,4 @@ public interface OpenFeatureHooks { * Removes all hooks. */ void clearHooks(); -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java new file mode 100644 index 0000000..6ba9733 --- /dev/null +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java @@ -0,0 +1,15 @@ +package dev.openfeature.api; + +/** + * Interface for OpenFeature API lifecycle management operations. + * Provides cleanup and shutdown capabilities for proper resource management. + */ +public interface OpenFeatureLifecycle { + /** + * Shut down and reset the current status of OpenFeature API. + * This call cleans up all active providers and attempts to shut down internal + * event handling mechanisms. + * Once shut down is complete, API is reset and ready to use again. + */ + void shutdown(); +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java new file mode 100644 index 0000000..e5f94b1 --- /dev/null +++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java @@ -0,0 +1,31 @@ +package dev.openfeature.api; + +/** + * Interface for transaction context management operations. + * Provides transaction-scoped context propagation and management, + * allowing for context to be passed across multiple operations + * within the same transaction or thread boundary. + */ +public interface OpenFeatureTransactionContext { + /** + * Return the transaction context propagator. + * + * @return the current transaction context propagator + */ + TransactionContextPropagator getTransactionContextPropagator(); + + /** + * Sets the transaction context propagator. + * + * @param transactionContextPropagator the transaction context propagator to use + * @throws IllegalArgumentException if {@code transactionContextPropagator} is null + */ + void setTransactionContextPropagator(TransactionContextPropagator transactionContextPropagator); + + /** + * Sets the transaction context using the registered transaction context propagator. + * + * @param evaluationContext the evaluation context to set for the current transaction + */ + void setTransactionContext(EvaluationContext evaluationContext); +} diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java i/openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java similarity index 95% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java rename to openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java index 3e1cf4b..31a4b4e 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java @@ -1,9 +1,5 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; -import dev.openfeature.api.ErrorCode; -import dev.openfeature.api.FlagEvaluationDetails; -import dev.openfeature.api.HookContext; -import dev.openfeature.api.Reason; /** * The Telemetry class provides constants and methods for creating OpenTelemetry compliant * evaluation events. diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java i/openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java similarity index 92% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java rename to openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java index 6507b64..7024124 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java @@ -1,6 +1,4 @@ -package dev.openfeature.sdk; - -import dev.openfeature.api.EvaluationContext; +package dev.openfeature.api; /** * {@link TransactionContextPropagator} is responsible for persisting a transactional context diff --git c/openfeature-api/src/main/java/dev/openfeature/api/Value.java i/openfeature-api/src/main/java/dev/openfeature/api/Value.java index 57d4efd..e7be432 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/Value.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/Value.java @@ -306,8 +306,8 @@ public class Value implements Cloneable { } else if (object instanceof Structure) { return new Value((Structure) object); } else if (object instanceof List) { - return new Value( - ((List<Object>) object).stream().map(o -> Value.objectToValue(o)).collect(Collectors.toList())); + return new Value(((List<Object>) object) + .stream().map(o -> Value.objectToValue(o)).collect(Collectors.toList())); } else if (object instanceof Instant) { return new Value((Instant) object); } else if (object instanceof Map) { diff --git c/openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java similarity index 77% rename from openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java index d79d346..d4b2949 100644 --- c/openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java @@ -1,5 +1,17 @@ -package dev.openfeature.api; +package dev.openfeature.api.internal.noop; +import dev.openfeature.api.Client; +import dev.openfeature.api.ClientMetadata; +import dev.openfeature.api.EvaluationContext; +import dev.openfeature.api.EventDetails; +import dev.openfeature.api.FlagEvaluationDetails; +import dev.openfeature.api.FlagEvaluationOptions; +import dev.openfeature.api.Hook; +import dev.openfeature.api.ProviderEvent; +import dev.openfeature.api.ProviderState; +import dev.openfeature.api.Reason; +import dev.openfeature.api.TrackingEventDetails; +import dev.openfeature.api.Value; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -7,8 +19,10 @@ import java.util.function.Consumer; /** * No-operation implementation of Client that provides safe defaults. * All flag evaluations return default values and all operations are safe no-ops. + * + * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong> */ -class NoOpClient implements Client { +public class NoOpClient implements Client { @Override public ClientMetadata getMetadata() { @@ -55,7 +69,8 @@ class NoOpClient implements Client { } @Override - public FlagEvaluationDetails<Boolean> getBooleanDetails(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public FlagEvaluationDetails<Boolean> getBooleanDetails( + String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return getBooleanDetails(key, defaultValue); } @@ -70,7 +85,8 @@ class NoOpClient implements Client { } @Override - public Boolean getBooleanValue(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public Boolean getBooleanValue( + String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return defaultValue; } @@ -89,7 +105,8 @@ class NoOpClient implements Client { } @Override - public FlagEvaluationDetails<String> getStringDetails(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public FlagEvaluationDetails<String> getStringDetails( + String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return getStringDetails(key, defaultValue); } @@ -104,7 +121,8 @@ class NoOpClient implements Client { } @Override - public String getStringValue(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public String getStringValue( + String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return defaultValue; } @@ -123,7 +141,8 @@ class NoOpClient implements Client { } @Override - public FlagEvaluationDetails<Integer> getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public FlagEvaluationDetails<Integer> getIntegerDetails( + String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return getIntegerDetails(key, defaultValue); } @@ -138,7 +157,8 @@ class NoOpClient implements Client { } @Override - public Integer getIntegerValue(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public Integer getIntegerValue( + String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return defaultValue; } @@ -157,7 +177,8 @@ class NoOpClient implements Client { } @Override - public FlagEvaluationDetails<Double> getDoubleDetails(String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public FlagEvaluationDetails<Double> getDoubleDetails( + String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return getDoubleDetails(key, defaultValue); } @@ -172,7 +193,8 @@ class NoOpClient implements Client { } @Override - public Double getDoubleValue(String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public Double getDoubleValue( + String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return defaultValue; } @@ -191,7 +213,8 @@ class NoOpClient implements Client { } @Override - public FlagEvaluationDetails<Value> getObjectDetails(String key, Value defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + public FlagEvaluationDetails<Value> getObjectDetails( + String key, Value defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return getObjectDetails(key, defaultValue); } @@ -259,4 +282,4 @@ class NoOpClient implements Client { public Client removeHandler(ProviderEvent event, Consumer<EventDetails> handler) { return this; // No-op - return self for chaining } -} \ No newline at end of file +} diff --git c/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java new file mode 100644 index 0000000..d3bdf95 --- /dev/null +++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java @@ -0,0 +1,160 @@ +package dev.openfeature.api.internal.noop; + +import dev.openfeature.api.Client; +import dev.openfeature.api.EvaluationContext; +import dev.openfeature.api.EventDetails; +import dev.openfeature.api.FeatureProvider; +import dev.openfeature.api.Hook; +import dev.openfeature.api.Metadata; +import dev.openfeature.api.OpenFeatureAPI; +import dev.openfeature.api.ProviderEvent; +import dev.openfeature.api.TransactionContextPropagator; +import dev.openfeature.api.exceptions.OpenFeatureError; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +/** + * No-operation implementation of OpenFeatureAPI that provides safe defaults. + * Used as a fallback when no actual implementation is available via ServiceLoader. + * All operations are safe no-ops that won't affect application functionality. + * + * <p>Package-private to prevent direct instantiation by external users. + */ +public class NoOpOpenFeatureAPI extends OpenFeatureAPI { + + private static final NoOpClient NO_OP_CLIENT = new NoOpClient(); + private static final NoOpProvider NO_OP_PROVIDER = new NoOpProvider(); + private static final NoOpTransactionContextPropagator NO_OP_TRANSACTION_CONTEXT_PROPAGATOR = + new NoOpTransactionContextPropagator(); + + @Override + public Client getClient() { + return NO_OP_CLIENT; + } + + @Override + public Client getClient(String domain) { + return NO_OP_CLIENT; + } + + @Override + public Client getClient(String domain, String version) { + return NO_OP_CLIENT; + } + + @Override + public void setProvider(FeatureProvider provider) { + // No-op - silently ignore + } + + @Override + public void setProvider(String domain, FeatureProvider provider) { + // No-op - silently ignore + } + + @Override + public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError { + // No-op - silently ignore + } + + @Override + public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError { + // No-op - silently ignore + } + + @Override + public FeatureProvider getProvider() { + return NO_OP_PROVIDER; + } + + @Override + public FeatureProvider getProvider(String domain) { + return NO_OP_PROVIDER; + } + + @Override + public Metadata getProviderMetadata() { + return () -> "No-op Provider"; + } + + @Override + public Metadata getProviderMetadata(String domain) { + return getProviderMetadata(); + } + + @Override + public void addHooks(Hook... hooks) { + // No-op - silently ignore + } + + @Override + public List<Hook> getHooks() { + return Collections.emptyList(); + } + + @Override + public void clearHooks() { + // No-op - nothing to clear + } + + @Override + public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) { + return this; // No-op - return self for chaining + } + + @Override + public EvaluationContext getEvaluationContext() { + return EvaluationContext.EMPTY; + } + + @Override + public OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler) { + return this; + } + + @Override + public TransactionContextPropagator getTransactionContextPropagator() { + return NO_OP_TRANSACTION_CONTEXT_PROPAGATOR; + } + + @Override + public void setTransactionContextPropagator(TransactionContextPropagator transactionContextPropagator) { + // No-op - silently ignore + } + + @Override + public void setTransactionContext(EvaluationContext evaluationContext) { + // No-op - silently ignore + } + + @Override + public void shutdown() { + // No-op - silently ignore + } + + @Override + public OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler) { + return this; + } + + @Override + public OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler) { + return this; + } + + @Override + public OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler) { + return this; + } + + @Override + public OpenFeatureAPI onProviderError(Consumer<EventDetails> handler) { + return this; + } + + @Override + public OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler) { + return this; + } +} diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java similarity index 94% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java index d65041a..35c9b5d 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api.internal.noop; import dev.openfeature.api.EvaluationContext; import dev.openfeature.api.FeatureProvider; @@ -11,6 +11,8 @@ import lombok.Getter; /** * A {@link FeatureProvider} that simply returns the default values passed to it. + * + * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong> */ public class NoOpProvider implements FeatureProvider { public static final String PASSED_IN_DEFAULT = "Passed in default"; diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java similarity index 73% rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java index 0f1a71b..3dd64bf 100644 --- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java +++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java @@ -1,9 +1,13 @@ -package dev.openfeature.sdk; +package dev.openfeature.api.internal.noop; import dev.openfeature.api.EvaluationContext; import dev.openfeature.api.ImmutableContext; +import dev.openfeature.api.TransactionContextPropagator; + /** * A {@link TransactionContextPropagator} that simply returns empty context. + * + * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong> */ public class NoOpTransactionContextPropagator implements TransactionContextPropagator { diff --git c/openfeature-api/src/main/java/module-info.java i/openfeature-api/src/main/java/module-info.java new file mode 100644 index 0000000..95c41e5 --- /dev/null +++ i/openfeature-api/src/main/java/module-info.java @@ -0,0 +1,14 @@ +module dev.openfeature.api { + requires static lombok; + requires org.slf4j; + requires com.github.spotbugs.annotations; + + exports dev.openfeature.api; + exports dev.openfeature.api.exceptions; + exports dev.openfeature.api.internal.noop; + + uses dev.openfeature.api.OpenFeatureAPIProvider; + + opens dev.openfeature.api to lombok; + opens dev.openfeature.api.exceptions to lombok; +} diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java i/openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java index 345a7ef..3539636 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java i/openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java similarity index 99% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java index 2291266..b4c637b 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java index 2b39be7..8ae55d2 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java @@ -1,6 +1,6 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; -import static dev.openfeature.sdk.EvaluationContext.TARGETING_KEY; +import static dev.openfeature.api.EvaluationContext.TARGETING_KEY; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java similarity index 97% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java index 5f176f1..db33f08 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java similarity index 99% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java index 6a0eed5..63f2702 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java i/openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java index 6c471d0..a9a8714 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java @@ -1,6 +1,6 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; -import static dev.openfeature.sdk.EvaluationContext.TARGETING_KEY; +import static dev.openfeature.api.EvaluationContext.TARGETING_KEY; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java index ebd11af..91f473c 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.*; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java index 2476243..2040c63 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java similarity index 98% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java index 2a2406a..3c15e01 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java @@ -1,6 +1,6 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; -import static dev.openfeature.sdk.Structure.mapToStructure; +import static dev.openfeature.api.Structure.mapToStructure; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java similarity index 99% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java index 697edb7..788c3f6 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.sdk; +package dev.openfeature.api; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java i/openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java similarity index 96% rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java rename to openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java index 0a9a522..0021571 100644 --- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java +++ i/openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java @@ -1,9 +1,9 @@ -package dev.openfeature.sdk.exceptions; +package dev.openfeature.api.exceptions; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import dev.openfeature.sdk.ErrorCode; +import dev.openfeature.api.ErrorCode; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.extension.ExtensionContext; diff --git c/openfeature-sdk/pom.xml i/openfeature-sdk/pom.xml index 6e4f367..3fa10b5 100644 --- c/openfeature-sdk/pom.xml +++ i/openfeature-sdk/pom.xml @@ -43,9 +43,88 @@ <scope>provided</scope> </dependency> - <!-- SLF4J already included from API module --> + <!-- SLF4J dependency needed for Lombok @Slf4j --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> - <!-- Test dependencies will be added later --> + <!-- Test dependencies (copied from parent) --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>5.13.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-suite</artifactId> + <version>1.13.4</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${org.mockito.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.27.3</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <version>4.3.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>io.cucumber</groupId> + <artifactId>cucumber-java</artifactId> + <version>7.27.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>io.cucumber</groupId> + <artifactId>cucumber-junit-platform-engine</artifactId> + <version>7.27.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.simplify4u</groupId> + <artifactId>slf4j2-mock</artifactId> + <version>2.4.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>33.4.8-jre</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>com.tngtech.archunit</groupId> + <artifactId>archunit-junit5</artifactId> + <version>1.4.1</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-core</artifactId> + <ver…
1 parent c6a039b commit 1231328

94 files changed

Lines changed: 1848 additions & 486 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchmark.txt

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
[INFO] Scanning for projects...
2-
[INFO]
2+
[INFO]
33
[INFO] ------------------------< dev.openfeature:sdk >-------------------------
44
[INFO] Building OpenFeature Java SDK 1.12.1
55
[INFO] from pom.xml
66
[INFO] --------------------------------[ jar ]---------------------------------
77
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
88
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
99
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
10-
[INFO]
10+
[INFO]
1111
[INFO] --- clean:3.2.0:clean (default-clean) @ sdk ---
1212
[INFO] Deleting /home/todd/git/java-sdk/target
13-
[INFO]
13+
[INFO]
1414
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
1515
[INFO] Starting audit...
1616
Audit done.
1717
[INFO] You have 0 Checkstyle violations.
18-
[INFO]
18+
[INFO]
1919
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
2020
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
21-
[INFO]
21+
[INFO]
2222
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
2323
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
24-
[INFO]
24+
[INFO]
2525
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
2626
[INFO] Recompiling the module because of changed source code.
2727
[INFO] Compiling 65 source files with javac [debug target 1.8] to target/classes
@@ -44,24 +44,24 @@ Audit done.
4444
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java: Recompile with -Xlint:deprecation for details.
4545
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Some input files use unchecked or unsafe operations.
4646
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Recompile with -Xlint:unchecked for details.
47-
[INFO]
47+
[INFO]
4848
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
4949
[INFO] Starting audit...
5050
Audit done.
5151
[INFO] You have 0 Checkstyle violations.
52-
[INFO]
52+
[INFO]
5353
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
5454
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
55-
[INFO]
55+
[INFO]
5656
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
5757
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
58-
[INFO]
58+
[INFO]
5959
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
6060
[INFO] Nothing to compile - all classes are up to date.
61-
[INFO]
61+
[INFO]
6262
[INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk ---
6363
[INFO] Copying 2 resources from src/test/resources to target/test-classes
64-
[INFO]
64+
[INFO]
6565
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ sdk ---
6666
[INFO] Recompiling the module because of changed dependency.
6767
[INFO] Compiling 52 source files with javac [debug target 1.8] to target/test-classes
@@ -80,29 +80,29 @@ Audit done.
8080
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/EventsTest.java: Recompile with -Xlint:deprecation for details.
8181
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Some input files use unchecked or unsafe operations.
8282
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Recompile with -Xlint:unchecked for details.
83-
[INFO]
83+
[INFO]
8484
[INFO] >>> jmh:0.2.2:benchmark (default-cli) > process-test-resources @ sdk >>>
85-
[INFO]
85+
[INFO]
8686
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
8787
[INFO] Starting audit...
8888
Audit done.
8989
[INFO] You have 0 Checkstyle violations.
90-
[INFO]
90+
[INFO]
9191
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
9292
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
93-
[INFO]
93+
[INFO]
9494
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
9595
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
96-
[INFO]
96+
[INFO]
9797
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
9898
[INFO] Nothing to compile - all classes are up to date.
99-
[INFO]
99+
[INFO]
100100
[INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk ---
101101
[INFO] Copying 2 resources from src/test/resources to target/test-classes
102-
[INFO]
102+
[INFO]
103103
[INFO] <<< jmh:0.2.2:benchmark (default-cli) < process-test-resources @ sdk <<<
104-
[INFO]
105-
[INFO]
104+
[INFO]
105+
[INFO]
106106
[INFO] --- jmh:0.2.2:benchmark (default-cli) @ sdk ---
107107
[INFO] Changes detected - recompiling the module!
108108
[INFO] Compiling 52 source files to /home/todd/git/java-sdk/target/test-classes
@@ -150,7 +150,7 @@ Iteration 1: num #instances #bytes class name (module)
150150
19: 149 1884376 [Ljdk.internal.vm.FillerElement; (java.base@21.0.4)
151151
20: 56476 1807232 java.util.ArrayList$Itr (java.base@21.0.4)
152152
21: 37481 1799088 dev.openfeature.sdk.FlagEvaluationDetails$FlagEvaluationDetailsBuilder
153-
22: 100001 1600016 dev.openfeature.sdk.NoOpProvider$$Lambda/0x000076e79c02fa78
153+
22: 100001 1600016 dev.openfeature.api.NoOpProvider$$Lambda/0x000076e79c02fa78
154154
23: 50000 1600000 [Ldev.openfeature.sdk.EvaluationContext;
155155
24: 50000 1600000 [Ljava.util.List; (java.base@21.0.4)
156156
25: 100000 1600000 dev.openfeature.sdk.OpenFeatureClient$$Lambda/0x000076e79c082800

openfeature-api/pom.xml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<version>4.8.6</version>
4343
<scope>provided</scope>
4444
</dependency>
45-
45+
4646
<!-- Test dependencies -->
4747
<dependency>
4848
<groupId>org.junit.jupiter</groupId>
@@ -77,7 +77,39 @@
7777
</archive>
7878
</configuration>
7979
</plugin>
80+
<plugin>
81+
<groupId>org.jacoco</groupId>
82+
<artifactId>jacoco-maven-plugin</artifactId>
83+
<version>0.8.13</version>
84+
<executions>
85+
<execution>
86+
<id>jacoco-check</id>
87+
<goals>
88+
<goal>check</goal>
89+
</goals>
90+
<configuration>
91+
<dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
92+
<excludes>
93+
<exclude>dev/openfeature/api/exceptions/**</exclude>
94+
<exclude>dev/openfeature/api/internal/**</exclude>
95+
</excludes>
96+
<rules>
97+
<rule>
98+
<element>PACKAGE</element>
99+
<limits>
100+
<limit>
101+
<counter>LINE</counter>
102+
<value>COVEREDRATIO</value>
103+
<minimum>0.3</minimum>
104+
</limit>
105+
</limits>
106+
</rule>
107+
</rules>
108+
</configuration>
109+
</execution>
110+
</executions>
111+
</plugin>
80112
</plugins>
81113
</build>
82114

83-
</project>
115+
</project>

openfeature-api/src/lombok.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lombok.addLombokGeneratedAnnotation = true
2+
lombok.extern.findbugs.addSuppressFBWarnings = true

openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java renamed to openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.openfeature.sdk;
1+
package dev.openfeature.api;
22

33
/**
44
* A class to help with synchronization by allowing the optional awaiting of the associated action.

openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
public interface EvaluationContext extends Structure {
1313

1414
String TARGETING_KEY = "targetingKey";
15-
15+
1616
/**
1717
* Empty evaluation context for use as a default.
1818
*/

openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java renamed to openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.openfeature.sdk;
1+
package dev.openfeature.api;
22

33
import java.util.HashMap;
44
import java.util.Map;

openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,32 @@
1111
@Data
1212
@SuperBuilder(toBuilder = true)
1313
public class EventDetails extends ProviderEventDetails {
14+
/** The domain associated with this event. */
1415
private String domain;
16+
17+
/** The name of the provider that generated this event. */
1518
private String providerName;
1619

17-
public static EventDetails fromProviderEventDetails(ProviderEventDetails providerEventDetails, String providerName) {
20+
/**
21+
* Create EventDetails from ProviderEventDetails with provider name.
22+
*
23+
* @param providerEventDetails the provider event details
24+
* @param providerName the name of the provider
25+
* @return EventDetails instance
26+
*/
27+
public static EventDetails fromProviderEventDetails(
28+
ProviderEventDetails providerEventDetails, String providerName) {
1829
return fromProviderEventDetails(providerEventDetails, providerName, null);
1930
}
2031

32+
/**
33+
* Create EventDetails from ProviderEventDetails with provider name and domain.
34+
*
35+
* @param providerEventDetails the provider event details
36+
* @param providerName the name of the provider
37+
* @param domain the domain associated with the event
38+
* @return EventDetails instance
39+
*/
2140
public static EventDetails fromProviderEventDetails(
2241
ProviderEventDetails providerEventDetails, String providerName, String domain) {
2342
return builder()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dev.openfeature.api;
2+
3+
/**
4+
* Interface for feature providers that support real-time events.
5+
* Providers can implement this interface to emit events about flag changes,
6+
* provider state changes, and other configuration updates.
7+
*
8+
* @see FeatureProvider
9+
*/
10+
public interface EventProvider extends FeatureProvider {
11+
12+
/**
13+
* Emit the specified {@link ProviderEvent}.
14+
*
15+
* @param event The event type
16+
* @param details The details of the event
17+
* @return An {@link Awaitable} that can be used to wait for event processing completion
18+
*/
19+
Awaitable emit(ProviderEvent event, ProviderEventDetails details);
20+
21+
/**
22+
* Emit a {@link ProviderEvent#PROVIDER_READY} event.
23+
* Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
24+
*
25+
* @param details The details of the event
26+
* @return An {@link Awaitable} that can be used to wait for event processing completion
27+
*/
28+
default Awaitable emitProviderReady(ProviderEventDetails details) {
29+
return emit(ProviderEvent.PROVIDER_READY, details);
30+
}
31+
32+
/**
33+
* Emit a {@link ProviderEvent#PROVIDER_CONFIGURATION_CHANGED} event.
34+
* Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
35+
*
36+
* @param details The details of the event
37+
* @return An {@link Awaitable} that can be used to wait for event processing completion
38+
*/
39+
default Awaitable emitProviderConfigurationChanged(ProviderEventDetails details) {
40+
return emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
41+
}
42+
43+
/**
44+
* Emit a {@link ProviderEvent#PROVIDER_STALE} event.
45+
* Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
46+
*
47+
* @param details The details of the event
48+
* @return An {@link Awaitable} that can be used to wait for event processing completion
49+
*/
50+
default Awaitable emitProviderStale(ProviderEventDetails details) {
51+
return emit(ProviderEvent.PROVIDER_STALE, details);
52+
}
53+
54+
/**
55+
* Emit a {@link ProviderEvent#PROVIDER_ERROR} event.
56+
* Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
57+
*
58+
* @param details The details of the event
59+
* @return An {@link Awaitable} that can be used to wait for event processing completion
60+
*/
61+
default Awaitable emitProviderError(ProviderEventDetails details) {
62+
return emit(ProviderEvent.PROVIDER_ERROR, details);
63+
}
64+
}

openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/**
77
* The interface implemented by upstream flag providers to resolve flags for
88
* their service. If you want to support realtime events with your provider, you
9-
* should extend the EventProvider class from the SDK module
9+
* should implement {@link EventProvider}
1010
*/
1111
public interface FeatureProvider {
1212
Metadata getMetadata();

openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)