From 6162f28e8d60f489e3e4b35cc48e3cf5af12a7a0 Mon Sep 17 00:00:00 2001 From: man85 Date: Thu, 2 Apr 2026 20:49:32 +0300 Subject: [PATCH 1/2] ISSUE-1919 Fixed: Domain client event handlers no longer disappear Signed-off-by: man85 --- .../dev/openfeature/sdk/EventSupport.java | 3 +- .../openfeature/sdk/OpenFeatureAPITest.java | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/openfeature/sdk/EventSupport.java b/src/main/java/dev/openfeature/sdk/EventSupport.java index 1a7c4d870..1458a610f 100644 --- a/src/main/java/dev/openfeature/sdk/EventSupport.java +++ b/src/main/java/dev/openfeature/sdk/EventSupport.java @@ -2,6 +2,7 @@ import dev.openfeature.sdk.internal.ConfigurableThreadFactory; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -117,7 +118,7 @@ public void removeGlobalHandler(ProviderEvent event, Consumer hand * @return set of domain names */ public Set getAllDomainNames() { - return this.handlerStores.keySet(); + return new HashSet<>(this.handlerStores.keySet()); } /** diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java index 2fdb4e3f0..6906ae67f 100644 --- a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java +++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java @@ -1,16 +1,22 @@ package dev.openfeature.sdk; +import static dev.openfeature.sdk.ProviderEvent.PROVIDER_CONFIGURATION_CHANGED; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import dev.openfeature.sdk.testutils.testProvider.TestProvider; import java.util.HashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class OpenFeatureAPITest { @@ -115,4 +121,42 @@ void featureProviderTrackIsCalled() throws Exception { verify(featureProvider, times(2)).getMetadata(); verify(featureProvider).track(any(), any(), any()); } + + @Test + @DisplayName("Domainless provider initialized after domain provider - OnConfigurationChanged handlers are invoked for both providers after emitting PROVIDER_CONFIGURATION_CHANGED") + void onConfigurationChangedAreCalledForDomainlessAndDomainProviders() throws InterruptedException { + EventProvider domainlessFeatureProvider = spy(EventProvider.class); + EventProvider featureProvider = spy(EventProvider.class); + + Client domainlessClient = api.getClient(); + Client client = api.getClient("domain"); + + CountDownLatch domainlessLatch = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + domainlessClient.onProviderConfigurationChanged( + eventDetails -> domainlessLatch.countDown() + ); + client.onProviderConfigurationChanged( + eventDetails -> latch.countDown() + ); + + api.setProviderAndWait("domain", featureProvider); + api.setProviderAndWait(domainlessFeatureProvider); + + featureProvider.emit(PROVIDER_CONFIGURATION_CHANGED, mock(ProviderEventDetails.class)); + domainlessFeatureProvider.emit(PROVIDER_CONFIGURATION_CHANGED, mock(ProviderEventDetails.class)); + + boolean domainlessCompleted = domainlessLatch.await(5, TimeUnit.SECONDS); + assertTrue( + domainlessCompleted, + "onProviderConfigurationChanged was not be invoked for a client with default domain" + ); + + boolean completed = latch.await(5, TimeUnit.SECONDS); + assertTrue( + completed, + "onProviderConfigurationChanged was not be invoked for a client with domain 'domain'" + ); + } } From f9bd55ee18f6fadb4b48a6e4e22fde7c90de1615 Mon Sep 17 00:00:00 2001 From: man85 Date: Mon, 6 Apr 2026 21:26:21 +0300 Subject: [PATCH 2/2] ISSUE-1919 Fixed: Domain client event handlers no longer disappear Signed-off-by: man85 --- .../openfeature/sdk/OpenFeatureAPITest.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java index 6906ae67f..b1f5b6ed8 100644 --- a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java +++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java @@ -123,7 +123,8 @@ void featureProviderTrackIsCalled() throws Exception { } @Test - @DisplayName("Domainless provider initialized after domain provider - OnConfigurationChanged handlers are invoked for both providers after emitting PROVIDER_CONFIGURATION_CHANGED") + @DisplayName( + "Domainless provider initialized after domain provider - OnConfigurationChanged handlers are invoked for both providers after emitting PROVIDER_CONFIGURATION_CHANGED") void onConfigurationChangedAreCalledForDomainlessAndDomainProviders() throws InterruptedException { EventProvider domainlessFeatureProvider = spy(EventProvider.class); EventProvider featureProvider = spy(EventProvider.class); @@ -134,12 +135,8 @@ void onConfigurationChangedAreCalledForDomainlessAndDomainProviders() throws Int CountDownLatch domainlessLatch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1); - domainlessClient.onProviderConfigurationChanged( - eventDetails -> domainlessLatch.countDown() - ); - client.onProviderConfigurationChanged( - eventDetails -> latch.countDown() - ); + domainlessClient.onProviderConfigurationChanged(eventDetails -> domainlessLatch.countDown()); + client.onProviderConfigurationChanged(eventDetails -> latch.countDown()); api.setProviderAndWait("domain", featureProvider); api.setProviderAndWait(domainlessFeatureProvider); @@ -150,13 +147,9 @@ void onConfigurationChangedAreCalledForDomainlessAndDomainProviders() throws Int boolean domainlessCompleted = domainlessLatch.await(5, TimeUnit.SECONDS); assertTrue( domainlessCompleted, - "onProviderConfigurationChanged was not be invoked for a client with default domain" - ); + "onProviderConfigurationChanged was not be invoked for a client with default domain"); boolean completed = latch.await(5, TimeUnit.SECONDS); - assertTrue( - completed, - "onProviderConfigurationChanged was not be invoked for a client with domain 'domain'" - ); + assertTrue(completed, "onProviderConfigurationChanged was not be invoked for a client with domain 'domain'"); } }