From 9ec51933536941b8768988aa32c6b0b80609824d Mon Sep 17 00:00:00 2001 From: jarebudev <23311805+jarebudev@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:04:09 +0000 Subject: [PATCH 1/3] flagd provider creates named daemon threads for error executor Signed-off-by: jarebudev <23311805+jarebudev@users.noreply.github.com> --- .../providers/flagd/FlagdProvider.java | 4 +-- .../providers/flagd/FlagdThreadFactory.java | 30 ++++++++++++++++ .../flagd/FlagdThreadFactoryTest.java | 34 +++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java create mode 100644 providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java index 4ce6e06ee..caf864175 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java @@ -91,7 +91,7 @@ public FlagdProvider(final FlagdOptions options) { } hooks.add(new SyncMetadataHook(this::getEnrichedContext)); contextEnricher = options.getContextEnricher(); - errorExecutor = Executors.newSingleThreadScheduledExecutor(); + errorExecutor = Executors.newSingleThreadScheduledExecutor(new FlagdThreadFactory("flagd-provider-thread")); gracePeriod = options.getRetryGracePeriod(); deadline = options.getDeadline(); } @@ -105,7 +105,7 @@ public FlagdProvider(final FlagdOptions options) { deadline = Config.DEFAULT_DEADLINE; gracePeriod = Config.DEFAULT_STREAM_RETRY_GRACE_PERIOD; hooks.add(new SyncMetadataHook(this::getEnrichedContext)); - errorExecutor = Executors.newSingleThreadScheduledExecutor(); + errorExecutor = Executors.newSingleThreadScheduledExecutor(new FlagdThreadFactory("flagd-provider-thread")); if (initialized) { this.syncResources.initialize(); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java new file mode 100644 index 000000000..a4fef470d --- /dev/null +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java @@ -0,0 +1,30 @@ +package dev.openfeature.contrib.providers.flagd; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Thread factory for the Flagd provider to allow named daemon threads to be created. + */ +public final class FlagdThreadFactory implements ThreadFactory { + + private final AtomicInteger counter = new AtomicInteger(); + private final String namePrefix; + + /** + * {@link FlagdThreadFactory}'s constructor. + * + * @param namePrefix Prefix used for setting the new thread's name. + */ + FlagdThreadFactory(String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public Thread newThread(Runnable runnable) { + final Thread thread = new Thread(runnable); + thread.setDaemon(true); + thread.setName(namePrefix + "-" + counter.incrementAndGet()); + return thread; + } +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java new file mode 100644 index 000000000..132a223ea --- /dev/null +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java @@ -0,0 +1,34 @@ +package dev.openfeature.contrib.providers.flagd; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class FlagdThreadFactoryTest { + + private static final String THREAD_NAME = "testthread"; + private final Runnable runnable = () -> {}; + + @Test + void verifyNewThreadHasNamePrefix() { + + var flagdThreadFactory = new FlagdThreadFactory(THREAD_NAME); + var thread = flagdThreadFactory.newThread(runnable); + + assertThat(thread.getName()).isEqualTo(THREAD_NAME + "-1"); + assertThat(thread.isDaemon()).isTrue(); + } + + @Test + void verifyNewThreadHasNamePrefixWithIncrement() { + + var flagdThreadFactory = new FlagdThreadFactory(THREAD_NAME); + var threadOne = flagdThreadFactory.newThread(runnable); + var threadTwo = flagdThreadFactory.newThread(runnable); + + assertThat(threadOne.getName()).isEqualTo(THREAD_NAME + "-1"); + assertThat(threadOne.isDaemon()).isTrue(); + assertThat(threadTwo.getName()).isEqualTo(THREAD_NAME + "-2"); + assertThat(threadTwo.isDaemon()).isTrue(); + } +} From 4a85e24abf5a981a4f5c7a19bd74c850fe1dd4d7 Mon Sep 17 00:00:00 2001 From: jarebudev <23311805+jarebudev@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:07:40 +0000 Subject: [PATCH 2/3] removed public modifier Signed-off-by: jarebudev <23311805+jarebudev@users.noreply.github.com> --- .../openfeature/contrib/providers/flagd/FlagdThreadFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java index a4fef470d..58d3b0c5a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java @@ -6,7 +6,7 @@ /** * Thread factory for the Flagd provider to allow named daemon threads to be created. */ -public final class FlagdThreadFactory implements ThreadFactory { +class FlagdThreadFactory implements ThreadFactory { private final AtomicInteger counter = new AtomicInteger(); private final String namePrefix; From 8a32d3b8fac3a5d41dd08b02008b1f9452882ffe Mon Sep 17 00:00:00 2001 From: jarebudev <23311805+jarebudev@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:50:35 +0000 Subject: [PATCH 3/3] applied suggested change for checking namePrefix param is not null Signed-off-by: jarebudev <23311805+jarebudev@users.noreply.github.com> --- .../contrib/providers/flagd/FlagdThreadFactory.java | 3 ++- .../providers/flagd/FlagdThreadFactoryTest.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java index 58d3b0c5a..54b54c719 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactory.java @@ -1,5 +1,6 @@ package dev.openfeature.contrib.providers.flagd; +import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; @@ -17,7 +18,7 @@ class FlagdThreadFactory implements ThreadFactory { * @param namePrefix Prefix used for setting the new thread's name. */ FlagdThreadFactory(String namePrefix) { - this.namePrefix = namePrefix; + this.namePrefix = Objects.requireNonNull(namePrefix, "namePrefix must not be null"); } @Override diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java index 132a223ea..442ac3631 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdThreadFactoryTest.java @@ -1,6 +1,7 @@ package dev.openfeature.contrib.providers.flagd; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -9,6 +10,17 @@ class FlagdThreadFactoryTest { private static final String THREAD_NAME = "testthread"; private final Runnable runnable = () -> {}; + @Test + void verifyThreadFactoryThrowsNullPointerExceptionWhenNamePrefixIsNull() { + + // Then + var exception = assertThrows(NullPointerException.class, () -> { + // When + new FlagdThreadFactory(null); + }); + assertThat(exception.toString()).contains("namePrefix must not be null"); + } + @Test void verifyNewThreadHasNamePrefix() {