diff --git a/src/main/java/dev/openfeature/sdk/EventSupport.java b/src/main/java/dev/openfeature/sdk/EventSupport.java index 3a55f298d..7620c3f83 100644 --- a/src/main/java/dev/openfeature/sdk/EventSupport.java +++ b/src/main/java/dev/openfeature/sdk/EventSupport.java @@ -145,7 +145,11 @@ public void runHandler(Consumer handler, EventDetails eventDetails * Stop the event handler task executor. */ public void shutdown() { - taskExecutor.shutdown(); + try { + taskExecutor.shutdown(); + } catch (Exception e) { + log.warn("Exception while attempting to shutdown task executor", e); + } } // Handler store maintains a set of handlers for each event type. diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java index 42ff4708c..c65b30fae 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java @@ -21,13 +21,15 @@ public class OpenFeatureAPI implements EventBus { // package-private multi-read/single-write lock static AutoCloseableReentrantReadWriteLock lock = new AutoCloseableReentrantReadWriteLock(); - private EvaluationContext evaluationContext; private final List apiHooks; - private ProviderRepository providerRepository = new ProviderRepository(); - private EventSupport eventSupport = new EventSupport(); + private ProviderRepository providerRepository; + private EventSupport eventSupport; + private EvaluationContext evaluationContext; protected OpenFeatureAPI() { apiHooks = new ArrayList<>(); + providerRepository = new ProviderRepository(); + eventSupport = new EventSupport(); } private static class SingletonHolder { @@ -190,9 +192,19 @@ public void clearHooks() { } } + /** + * Shutdown and reset the current status of OpenFeature API. + * This call cleans up all active providers and attempt to shut down internal event handling mechanisms. + * Once shutdown is complete, API is reset and ready to use again. + * */ public void shutdown() { - providerRepository.shutdown(); - eventSupport.shutdown(); + try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) { + providerRepository.shutdown(); + eventSupport.shutdown(); + + providerRepository = new ProviderRepository(); + eventSupport = new EventSupport(); + } } /** @@ -264,15 +276,6 @@ void addHandler(String clientName, ProviderEvent event, Consumer h } } - /** - * This method is only here for testing as otherwise all tests after the API - * shutdown test would fail. - */ - final void reset() { - providerRepository = new ProviderRepository(); - eventSupport = new EventSupport(); - } - /** * Runs the handlers associated with a particular provider. * diff --git a/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java b/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java index e470819f7..8659ce783 100644 --- a/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java @@ -109,9 +109,22 @@ void mustShutdownAllProvidersOnShuttingDownApi() { verify(defaultProvider).shutdown(); verify(namedProvider).shutdown(); }); - - api.reset(); } } + + + @Test + @DisplayName("once shutdown is complete, api must be ready to use again") + void apiIsReadyToUseAfterShutdown() { + final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance(); + + NoOpProvider p1 = new NoOpProvider(); + openFeatureAPI.setProvider(p1); + + openFeatureAPI.shutdown(); + + NoOpProvider p2 = new NoOpProvider(); + openFeatureAPI.setProvider(p2); + } } }