diff --git a/manifests/java.yml b/manifests/java.yml
index f8bf9193772..ccdfaf4d236 100644
--- a/manifests/java.yml
+++ b/manifests/java.yml
@@ -2113,7 +2113,7 @@ tests/:
parametric/:
test_feature_flag_exposure/:
test_feature_flag_exposure.py:
- Test_Feature_Flag_Exposure: missing_feature
+ Test_Feature_Flag_Exposure: v1.56.0
test_128_bit_traceids.py:
Test_128_Bit_Traceids: v1.12.0
test_config_consistency.py:
@@ -2150,7 +2150,7 @@ tests/:
Test_Parametric_DDTrace_Baggage: incomplete_test_app (baggage endpoints are not implemented)
Test_Parametric_DDTrace_Crash: bug (APMAPI-778) # The crash endpoint does not kill the application
Test_Parametric_DDTrace_Current_Span: bug (APMAPI-778) # Fails to retreive the current span after a span has finished
- Test_Parametric_FFE_Start: missing_feature
+ Test_Parametric_FFE_Start: v1.56.0
Test_Parametric_Otel_Baggage: missing_feature (otel baggage is not supported)
Test_Parametric_Otel_Current_Span: bug (APMAPI-778) # Current span endpoint does not return DataDog spans created by the otel api
Test_Parametric_Write_Log: missing_feature
@@ -2331,9 +2331,15 @@ tests/:
Test_Span_Links_From_Conflicting_Contexts: v1.43.0
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_feature_flag_exposures.py:
- Test_FFE_Exposure_Events: missing_feature
- Test_FFE_Exposure_Events_Empty: missing_feature
- Test_FFE_Exposure_Events_Errors: missing_feature
+ Test_FFE_Exposure_Events:
+ '*': irrelevant
+ spring-boot: v1.56.0
+ Test_FFE_Exposure_Events_Empty:
+ '*': irrelevant
+ spring-boot: v1.56.0
+ Test_FFE_Exposure_Events_Errors:
+ '*': irrelevant
+ spring-boot: v1.56.0
test_graphql.py:
Test_GraphQLOperationErrorReporting:
'*': missing_feature
diff --git a/utils/build/docker/java/install_ddtrace.sh b/utils/build/docker/java/install_ddtrace.sh
index 115af276c66..20d67c955ac 100755
--- a/utils/build/docker/java/install_ddtrace.sh
+++ b/utils/build/docker/java/install_ddtrace.sh
@@ -29,6 +29,9 @@ install_custom_jar() {
# Look for custom dd-trace-api jar in custom binaries folder
install_custom_jar "dd-trace-api*.jar" "dd-trace-api" "$MVN_OPTS"
+# Look for custom dd-openfeature jar in custom binaries folder
+install_custom_jar "dd-openfeature*.jar" "dd-openfeature" "$MVN_OPTS"
+
# Look for custom dd-trace-java jar in custom binaries folder
if [ $(ls /binaries/dd-java-agent*.jar | wc -l) = 0 ]; then
BUILD_URL="https://github.com/DataDog/dd-trace-java/releases/latest/download/dd-java-agent.jar"
diff --git a/utils/build/docker/java/parametric/install_ddtrace.sh b/utils/build/docker/java/parametric/install_ddtrace.sh
index c1bbf87695b..580bfc7c6e4 100755
--- a/utils/build/docker/java/parametric/install_ddtrace.sh
+++ b/utils/build/docker/java/parametric/install_ddtrace.sh
@@ -25,6 +25,9 @@ configure_custom_jar() {
# Look for custom dd-trace-api jar in custom binaries folder
configure_custom_jar "dd-trace-api*.jar" "dd-trace-api" "customDdTraceApi"
+# Look for custom dd-openfeature jar in custom binaries folder
+configure_custom_jar "dd-openfeature*.jar" "dd-openfeature" "customDdOpenfeature"
+
# Look for custom dd-java-agent jar in custom binaries folder
CUSTOM_DD_JAVA_AGENT_COUNT=$(find /binaries/dd-java-agent*.jar 2>/dev/null | wc -l)
if [ "$CUSTOM_DD_JAVA_AGENT_COUNT" = 0 ]; then
diff --git a/utils/build/docker/java/parametric/pom.xml b/utils/build/docker/java/parametric/pom.xml
index cd2b9f93084..1351080e7c4 100644
--- a/utils/build/docker/java/parametric/pom.xml
+++ b/utils/build/docker/java/parametric/pom.xml
@@ -24,6 +24,7 @@
1.45.0
[1.47.0,)
+ [1.56.0,)
@@ -69,6 +70,12 @@
opentelemetry-api
${opentelemetry.version}
+
+
+ com.datadoghq
+ dd-openfeature
+ ${dd-openfeature.version}
+
org.apache.commons
commons-lang3
@@ -89,6 +96,11 @@
javax.mail
1.6.2
+
+ dev.openfeature
+ sdk
+ 1.18.2
+
@@ -122,5 +134,22 @@
+
+ custom-dd-openfeature
+
+
+ customDdOpenfeature
+
+
+
+
+ com.datadoghq
+ dd-openfeature
+ dev
+ system
+ ${customDdOpenfeature}
+
+
+
diff --git a/utils/build/docker/java/parametric/src/main/java/com/datadoghq/trace/controller/FeatureFlagEvaluatorController.java b/utils/build/docker/java/parametric/src/main/java/com/datadoghq/trace/controller/FeatureFlagEvaluatorController.java
new file mode 100644
index 00000000000..4fef048ef59
--- /dev/null
+++ b/utils/build/docker/java/parametric/src/main/java/com/datadoghq/trace/controller/FeatureFlagEvaluatorController.java
@@ -0,0 +1,186 @@
+package com.datadoghq.trace.controller;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import datadog.trace.api.openfeature.Provider;
+import dev.openfeature.sdk.Client;
+import dev.openfeature.sdk.EvaluationContext;
+import dev.openfeature.sdk.FeatureProvider;
+import dev.openfeature.sdk.MutableContext;
+import dev.openfeature.sdk.NoOpProvider;
+import dev.openfeature.sdk.OpenFeatureAPI;
+import dev.openfeature.sdk.ProviderState;
+import dev.openfeature.sdk.Structure;
+import dev.openfeature.sdk.Value;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/ffe")
+public class FeatureFlagEvaluatorController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(FeatureFlagEvaluatorController.class);
+
+ @Configuration
+ public static class FeatureFlagEvaluatorConfig {
+
+ @Lazy
+ @Bean
+ public Client client() {
+ final OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+ final String envProperty = System.getenv("DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED");
+ final FeatureProvider provider;
+ if (Boolean.parseBoolean(envProperty)) {
+ provider = new Provider();
+ } else {
+ provider = new NoOpProvider() {
+ @Override
+ public ProviderState getState() {
+ return ProviderState.READY;
+ }
+ };
+ }
+ api.setProviderAndWait(provider);
+ return api.getClient();
+ }
+ }
+
+ @Autowired
+ @Lazy
+ private Client client;
+
+ @PostMapping(value = "/start")
+ public ResponseEntity start() {
+ final ProviderState state = client.getProviderState();
+ if (state == ProviderState.READY) {
+ return ResponseEntity.ok(true);
+ } else {
+ return ResponseEntity.internalServerError().body(false);
+ }
+ }
+
+ @PostMapping(value = "/evaluate", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
+ public ResponseEntity