diff --git a/src/main/java/dev/openfeature/sdk/FeatureProviderStateManager.java b/src/main/java/dev/openfeature/sdk/FeatureProviderStateManager.java index 2c39ece6b..5fd70221b 100644 --- a/src/main/java/dev/openfeature/sdk/FeatureProviderStateManager.java +++ b/src/main/java/dev/openfeature/sdk/FeatureProviderStateManager.java @@ -2,14 +2,14 @@ import dev.openfeature.sdk.exceptions.OpenFeatureError; import java.util.concurrent.atomic.AtomicBoolean; -import lombok.Getter; +import java.util.concurrent.atomic.AtomicReference; +import lombok.extern.slf4j.Slf4j; +@Slf4j class FeatureProviderStateManager implements EventProviderListener { private final FeatureProvider delegate; private final AtomicBoolean isInitialized = new AtomicBoolean(); - - @Getter - private ProviderState state = ProviderState.NOT_READY; + private final AtomicReference state = new AtomicReference<>(ProviderState.NOT_READY); public FeatureProviderStateManager(FeatureProvider delegate) { this.delegate = delegate; @@ -24,17 +24,17 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { } try { delegate.initialize(evaluationContext); - state = ProviderState.READY; + setState(ProviderState.READY); } catch (OpenFeatureError openFeatureError) { if (ErrorCode.PROVIDER_FATAL.equals(openFeatureError.getErrorCode())) { - state = ProviderState.FATAL; + setState(ProviderState.FATAL); } else { - state = ProviderState.ERROR; + setState(ProviderState.ERROR); } isInitialized.set(false); throw openFeatureError; } catch (Exception e) { - state = ProviderState.ERROR; + setState(ProviderState.ERROR); isInitialized.set(false); throw e; } @@ -42,7 +42,7 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { public void shutdown() { delegate.shutdown(); - state = ProviderState.NOT_READY; + setState(ProviderState.NOT_READY); isInitialized.set(false); } @@ -50,17 +50,34 @@ public void shutdown() { public void onEmit(ProviderEvent event, ProviderEventDetails details) { if (ProviderEvent.PROVIDER_ERROR.equals(event)) { if (details != null && details.getErrorCode() == ErrorCode.PROVIDER_FATAL) { - state = ProviderState.FATAL; + setState(ProviderState.FATAL); } else { - state = ProviderState.ERROR; + setState(ProviderState.ERROR); } } else if (ProviderEvent.PROVIDER_STALE.equals(event)) { - state = ProviderState.STALE; + setState(ProviderState.STALE); } else if (ProviderEvent.PROVIDER_READY.equals(event)) { - state = ProviderState.READY; + setState(ProviderState.READY); + } + } + + private void setState(ProviderState state) { + ProviderState oldState = this.state.getAndSet(state); + if (oldState != state) { + String providerName; + if (delegate.getMetadata() == null || delegate.getMetadata().getName() == null) { + providerName = "unknown"; + } else { + providerName = delegate.getMetadata().getName(); + } + log.info("Provider {} transitioned from state {} to state {}", providerName, oldState, state); } } + public ProviderState getState() { + return state.get(); + } + FeatureProvider getProvider() { return delegate; } diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java index e8e8b27b0..66fd06d55 100644 --- a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java +++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import dev.openfeature.sdk.providers.memory.InMemoryProvider; @@ -112,7 +113,7 @@ void featureProviderTrackIsCalled() throws Exception { api.getClient().track("track-event", new ImmutableContext(), new MutableTrackingEventDetails(22.2f)); verify(featureProvider).initialize(any()); - verify(featureProvider).getMetadata(); + verify(featureProvider, times(2)).getMetadata(); verify(featureProvider).track(any(), any(), any()); } }