From d583cd219c1181e4bafeb7b79b0e8ed769921bed Mon Sep 17 00:00:00 2001 From: Tobias Ibounig Date: Fri, 5 Jun 2026 09:25:40 +0200 Subject: [PATCH 1/4] perf: skip unmodifiableMap wrapper when hookHints is empty Signed-off-by: Tobias Ibounig --- src/main/java/dev/openfeature/sdk/OpenFeatureClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java index 0d5d0e643..dbff5e8c3 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java @@ -170,7 +170,8 @@ private FlagEvaluationDetails evaluateFlag( flagOptions = options; } - hookSupportData.hints = Collections.unmodifiableMap(flagOptions.getHookHints()); + var hookHints = flagOptions.getHookHints(); + hookSupportData.hints = hookHints.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(hookHints); var context = new LayeredEvaluationContext( openfeatureApi.getEvaluationContext(), openfeatureApi.getTransactionContext(), From 796d5b760dc073d03f7c486bbe0620b72246bd54 Mon Sep 17 00:00:00 2001 From: Tobias Ibounig Date: Mon, 8 Jun 2026 17:34:49 +0200 Subject: [PATCH 2/4] ensure hookHints non null via builder, added test Signed-off-by: Tobias Ibounig --- .../dev/openfeature/sdk/FlagEvaluationOptions.java | 10 ++++++++++ src/test/java/dev/openfeature/sdk/HookSpecTest.java | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java b/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java index f73bd9631..d586a9371 100644 --- a/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java +++ b/src/main/java/dev/openfeature/sdk/FlagEvaluationOptions.java @@ -1,5 +1,6 @@ package dev.openfeature.sdk; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,4 +20,13 @@ public class FlagEvaluationOptions { @Builder.Default Map hookHints = new HashMap<>(); + + public static class FlagEvaluationOptionsBuilder { + /** Sets hook hints, normalizing null to an empty map. */ + public FlagEvaluationOptionsBuilder hookHints(Map hookHints) { + this.hookHints$value = hookHints != null ? hookHints : Collections.emptyMap(); + this.hookHints$set = true; + return this; + } + } } diff --git a/src/test/java/dev/openfeature/sdk/HookSpecTest.java b/src/test/java/dev/openfeature/sdk/HookSpecTest.java index 69e47f353..b9d6833d7 100644 --- a/src/test/java/dev/openfeature/sdk/HookSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/HookSpecTest.java @@ -510,6 +510,13 @@ void missing_hook_hints() { assertTrue(feo.getHookHints().isEmpty()); } + @Test + void null_hook_hints_does_not_throw() { + FlagEvaluationOptions feo = + FlagEvaluationOptions.builder().hookHints(null).build(); + assertThat(feo.getHookHints()).isNotNull().isEmpty(); + } + @Test void flag_eval_hook_order() { Hook hook = mockBooleanHook(); From 8a9fc31b9a6213c41b535e5f67cdb93ca01d5eda Mon Sep 17 00:00:00 2001 From: Tobias Ibounig Date: Tue, 9 Jun 2026 12:08:41 +0200 Subject: [PATCH 3/4] test null does not throw exception Signed-off-by: Tobias Ibounig --- .../java/dev/openfeature/sdk/HookSpecTest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/java/dev/openfeature/sdk/HookSpecTest.java b/src/test/java/dev/openfeature/sdk/HookSpecTest.java index b9d6833d7..a265ccade 100644 --- a/src/test/java/dev/openfeature/sdk/HookSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/HookSpecTest.java @@ -512,9 +512,20 @@ void missing_hook_hints() { @Test void null_hook_hints_does_not_throw() { - FlagEvaluationOptions feo = - FlagEvaluationOptions.builder().hookHints(null).build(); - assertThat(feo.getHookHints()).isNotNull().isEmpty(); + Hook hook = mockBooleanHook(); + FeatureProvider provider = mock(FeatureProvider.class); + when(provider.getBooleanEvaluation(any(), any(), any())) + .thenReturn(ProviderEvaluation.builder().value(true).build()); + + api.setProviderAndWait(provider); + Client client = api.getClient(); + + assertThatCode(() -> client.getBooleanValue( + "key", + false, + new ImmutableContext(), + FlagEvaluationOptions.builder().hook(hook).hookHints(null).build())) + .doesNotThrowAnyException(); } @Test From aad88666c306b464497a1e354210ea5cb4768f00 Mon Sep 17 00:00:00 2001 From: Tobias Ibounig Date: Tue, 9 Jun 2026 16:46:40 +0200 Subject: [PATCH 4/4] spotless Signed-off-by: Tobias Ibounig --- src/test/java/dev/openfeature/sdk/HookSpecTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/dev/openfeature/sdk/HookSpecTest.java b/src/test/java/dev/openfeature/sdk/HookSpecTest.java index a265ccade..480e81d32 100644 --- a/src/test/java/dev/openfeature/sdk/HookSpecTest.java +++ b/src/test/java/dev/openfeature/sdk/HookSpecTest.java @@ -524,7 +524,10 @@ void null_hook_hints_does_not_throw() { "key", false, new ImmutableContext(), - FlagEvaluationOptions.builder().hook(hook).hookHints(null).build())) + FlagEvaluationOptions.builder() + .hook(hook) + .hookHints(null) + .build())) .doesNotThrowAnyException(); }