diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 09e44b790e1..f7dec0f13c9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -399,9 +399,10 @@ **/kafka*/**/*Iast* @DataDog/asm-java @DataDog/data-streams-monitoring @DataDog/apm-idm-java # @DataDog/feature-flagging-and-experimentation-sdk -/dd-openfeature/ @DataDog/feature-flagging-and-experimentation-sdk -/dd-smoke-tests/openfeature/ @DataDog/feature-flagging-and-experimentation-sdk -/products/feature-flagging/ @DataDog/feature-flagging-and-experimentation-sdk +/dd-java-agent/instrumentation/datadog/openfeature/ @DataDog/feature-flagging-and-experimentation-sdk +/dd-openfeature/ @DataDog/feature-flagging-and-experimentation-sdk +/dd-smoke-tests/openfeature/ @DataDog/feature-flagging-and-experimentation-sdk +/products/feature-flagging/ @DataDog/feature-flagging-and-experimentation-sdk # @DataDog/profiling-java /dd-java-agent/ddprof-lib/ @DataDog/profiling-java diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/build.gradle b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/build.gradle new file mode 100644 index 00000000000..900c517f4b8 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'dd-trace-java.module.instrumentation' +} + +// TODO Add a muzzle pass directive on com.datadoghq:dd-openfeature once the SDK is published. + +tracerJava { + addSourceSetFor(JavaVersion.VERSION_11) +} + +testJvmConstraints { + minJavaVersion = JavaVersion.VERSION_11 +} + +dependencies { + main_java11CompileOnly project(':dd-openfeature') + main_java11CompileOnly project(':products:feature-flagging:feature-flagging-bootstrap') + main_java11CompileOnly libs.spotbugs.annotations + + testImplementation project(':dd-openfeature') + testImplementation project(':products:feature-flagging:feature-flagging-bootstrap') + testImplementation libs.bundles.mockito +} + +tasks.named("compileMain_java11Java", JavaCompile) { + configureCompiler(it, JavaVersion.VERSION_11) +} + +tasks.named("compileTestJava", JavaCompile) { + configureCompiler(it, JavaVersion.VERSION_11) +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/gradle.lockfile b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/gradle.lockfile new file mode 100644 index 00000000000..766eb3c9ac9 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/gradle.lockfile @@ -0,0 +1,132 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:datadog:openfeature:dd-openfeature-1.69:dependencies --write-locks +cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.13=testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.13=testCompileClasspath,testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-instrument-java:0.0.6=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-openfeature:1.67.0=legacySdk +com.datadoghq:java-dogstatsd-client:4.4.5=testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.20.0=main_java11CompileClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.20.0=main_java11CompileClasspath,testCompileClasspath,testRuntimeClasspath +com.github.javaparser:javaparser-core:3.25.6=codenarc +com.github.jnr:jffi:1.3.15=testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.20=testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.19=testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.22=testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.25=testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.10.4=compileClasspath,main_java11CompileClasspath,spotbugs +com.github.spotbugs:spotbugs:4.10.4=spotbugs +com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs +com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath +com.google.auto.service:auto-service:1.1.1=annotationProcessor,testAnnotationProcessor +com.google.auto:auto-common:1.2.1=annotationProcessor,testAnnotationProcessor +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,main_java11CompileClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.code.gson:gson:2.14.0=spotbugs +com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.48.0=spotbugs +com.google.guava:failureaccess:1.0.1=annotationProcessor,testAnnotationProcessor +com.google.guava:failureaccess:1.0.3=testCompileClasspath,testRuntimeClasspath +com.google.guava:guava:32.0.1-jre=annotationProcessor,testAnnotationProcessor +com.google.guava:guava:33.6.0-jre=testCompileClasspath,testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,testAnnotationProcessor +com.google.j2objc:j2objc-annotations:3.1=testCompileClasspath,testRuntimeClasspath +com.google.re2j:re2j:1.8=testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath +com.thoughtworks.qdox:qdox:1.12.1=codenarc +commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.21.0=spotbugs +de.thetaphi:forbiddenapis:3.11=compileClasspath,testCompileClasspath,testRuntimeClasspath +dev.openfeature:sdk:1.23.0=main_java11CompileClasspath,testCompileClasspath,testRuntimeClasspath +io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath +io.opentelemetry:opentelemetry-api:1.66.0=testRuntimeClasspath +io.opentelemetry:opentelemetry-common:1.66.0=testRuntimeClasspath +io.opentelemetry:opentelemetry-context:1.66.0=testRuntimeClasspath +io.sqreen:libsqreen:17.5.0=testRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath +jaxen:jaxen:2.0.6=spotbugs +junit:junit:4.13.2=testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath +net.java.dev.jna:jna:5.8.0=testRuntimeClasspath +net.sf.saxon:Saxon-HE:12.10=spotbugs +org.apache.ant:ant-antlr:1.10.14=codenarc +org.apache.ant:ant-junit:1.10.14=codenarc +org.apache.bcel:bcel:6.12.0=spotbugs +org.apache.commons:commons-lang3:3.20.0=spotbugs +org.apache.commons:commons-text:1.15.0=spotbugs +org.apache.logging.log4j:log4j-api:2.26.1=spotbugs +org.apache.logging.log4j:log4j-core:2.26.1=spotbugs +org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testRuntimeClasspath +org.checkerframework:checker-qual:3.33.0=annotationProcessor,testAnnotationProcessor +org.codehaus.groovy:groovy-ant:3.0.23=codenarc +org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc +org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-templates:3.0.23=codenarc +org.codehaus.groovy:groovy-xml:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codenarc:CodeNarc:3.7.0=codenarc +org.dom4j:dom4j:2.2.0=spotbugs +org.gmetrics:GMetrics:2.1.0=codenarc +org.hamcrest:hamcrest-core:1.3=testRuntimeClasspath +org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath +org.jctools:jctools-core-jdk11:4.0.6=testRuntimeClasspath +org.jctools:jctools-core:4.0.6=testRuntimeClasspath +org.jspecify:jspecify:1.0.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.14.1=testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.14.1=testRuntimeClasspath +org.junit:junit-bom:5.14.1=testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-core:4.4.0=testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-junit-jupiter:4.4.0=testCompileClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.10.1=spotbugs +org.ow2.asm:asm-analysis:9.7.1=testRuntimeClasspath +org.ow2.asm:asm-commons:9.10.1=spotbugs,testRuntimeClasspath +org.ow2.asm:asm-tree:9.10.1=spotbugs,testRuntimeClasspath +org.ow2.asm:asm-util:9.10.1=spotbugs +org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath +org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath +org.slf4j:slf4j-api:2.0.17=spotbugsSlf4j +org.slf4j:slf4j-api:2.0.18=spotbugs +org.slf4j:slf4j-api:2.0.19=main_java11CompileClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j +org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath +org.spockframework:spock-bom:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-core:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-junit:1.2.2=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-parser:1.2.1=testCompileClasspath,testRuntimeClasspath +org.xmlresolver:xmlresolver:5.3.3=spotbugs +empty=main_java11AnnotationProcessor,spotbugsPlugins diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentation.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentation.java new file mode 100644 index 00000000000..8fa3abcb5e7 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentation.java @@ -0,0 +1,55 @@ +package datadog.trace.instrumentation.openfeature; + +import static datadog.trace.agent.tooling.InstrumenterModule.TargetSystem.FEATURE_FLAGS; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.Set; + +/** + * Connects the {@code dd-openfeature} SDK to the agent: Remote Configuration, the event platform + * proxy, health metrics and span enrichment. + */ +@SuppressWarnings("unused") +@AutoService(InstrumenterModule.class) +public class DDOpenFeatureInstrumentation extends InstrumenterModule + implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { + + public DDOpenFeatureInstrumentation() { + super("openfeature"); + } + + @Override + public boolean isApplicable(final Set enabledSystems) { + return enabledSystems.contains(FEATURE_FLAGS); + } + + @Override + public String instrumentedType() { + return "com.datadog.openfeature.internal.bridge.v1.AgentBridges"; + } + + @Override + public String[] helperClassNames() { + // Helpers are compiled in the java11 source set, out of reach of helper discovery. + return new String[] { + packageName + ".AgentConfigurationSource", + packageName + ".AgentEventProxy", + packageName + ".AgentHealthMetrics", + packageName + ".AgentSpanEnricher", + packageName + ".JavaAgentBridge", + }; + } + + @Override + public void methodAdvice(final MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(isStatic()).and(named("detect")).and(takesNoArguments()), + packageName + ".DetectBridgeAdvice"); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentConfigurationSource.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentConfigurationSource.java new file mode 100644 index 00000000000..4ae21393a20 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentConfigurationSource.java @@ -0,0 +1,19 @@ +package datadog.trace.instrumentation.openfeature; + +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import java.util.function.Consumer; + +/** Receives the flag configuration from the agent Remote Configuration client. */ +public final class AgentConfigurationSource implements ConfigurationSource { + private final FeatureFlaggingGateway.Backend backend; + + AgentConfigurationSource(final FeatureFlaggingGateway.Backend backend) { + this.backend = backend; + } + + @Override + public AutoCloseable subscribe(final Consumer listener) { + return this.backend.subscribeRemoteConfig(listener); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentEventProxy.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentEventProxy.java new file mode 100644 index 00000000000..9a3164adfcc --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentEventProxy.java @@ -0,0 +1,22 @@ +package datadog.trace.instrumentation.openfeature; + +import com.datadog.openfeature.internal.bridge.v1.EventProxyUnavailableException; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import java.io.IOException; + +/** Posts events through the Datadog Agent event platform proxy. */ +public final class AgentEventProxy implements EventTransport { + private final FeatureFlaggingGateway.Backend backend; + + AgentEventProxy(final FeatureFlaggingGateway.Backend backend) { + this.backend = backend; + } + + @Override + public void post(final String route, final byte[] json) throws IOException { + if (!this.backend.postEvent(route, json)) { + throw new EventProxyUnavailableException("Datadog Agent event platform proxy unavailable"); + } + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentHealthMetrics.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentHealthMetrics.java new file mode 100644 index 00000000000..b809995f8a5 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentHealthMetrics.java @@ -0,0 +1,19 @@ +package datadog.trace.instrumentation.openfeature; + +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import javax.annotation.Nullable; + +/** Reports the SDK health counters through the agent telemetry. */ +public final class AgentHealthMetrics implements HealthMetrics { + private final FeatureFlaggingGateway.Backend backend; + + AgentHealthMetrics(final FeatureFlaggingGateway.Backend backend) { + this.backend = backend; + } + + @Override + public void count(final String metric, final long value, @Nullable final String reason) { + this.backend.count(metric, value, reason); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentSpanEnricher.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentSpanEnricher.java new file mode 100644 index 00000000000..f7a3cb2ccca --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/AgentSpanEnricher.java @@ -0,0 +1,25 @@ +package datadog.trace.instrumentation.openfeature; + +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import javax.annotation.Nullable; + +/** Reflects flag evaluations onto the agent local root span. */ +public final class AgentSpanEnricher implements SpanEnricher { + private final FeatureFlaggingGateway.Backend backend; + + AgentSpanEnricher(final FeatureFlaggingGateway.Backend backend) { + this.backend = backend; + } + + @Override + public void serialId( + final int serialId, final boolean doLog, @Nullable final String targetingKey) { + this.backend.enrichSerialId(serialId, doLog, targetingKey); + } + + @Override + public void runtimeDefault(final String flagKey, @Nullable final Object defaultValue) { + this.backend.enrichRuntimeDefault(flagKey, defaultValue); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/DetectBridgeAdvice.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/DetectBridgeAdvice.java new file mode 100644 index 00000000000..3be76c444b6 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/DetectBridgeAdvice.java @@ -0,0 +1,14 @@ +package datadog.trace.instrumentation.openfeature; + +import static net.bytebuddy.asm.Advice.OnMethodExit; +import static net.bytebuddy.asm.Advice.Return; + +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; + +@SuppressWarnings("unused") +public class DetectBridgeAdvice { + @OnMethodExit(suppress = Throwable.class) + public static void detect(@Return(readOnly = false) AgentBridge bridge) { + bridge = JavaAgentBridge.connect(bridge); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/JavaAgentBridge.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/JavaAgentBridge.java new file mode 100644 index 00000000000..a0122636ff5 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/main/java11/datadog/trace/instrumentation/openfeature/JavaAgentBridge.java @@ -0,0 +1,70 @@ +package datadog.trace.instrumentation.openfeature; + +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import javax.annotation.Nullable; + +/** The bridge backed by the agent Feature Flags backend. */ +public final class JavaAgentBridge implements AgentBridge { + private final FeatureFlaggingGateway.Backend backend; + private final HealthMetrics healthMetrics; + private final SpanEnricher spanEnricher; + + JavaAgentBridge(final FeatureFlaggingGateway.Backend backend) { + this.backend = backend; + this.healthMetrics = new AgentHealthMetrics(backend); + this.spanEnricher = new AgentSpanEnricher(backend); + } + + /** + * Connects the SDK to the agent backend. + * + * @param fallback the bridge to use if the agent backend is not started. + * @return the agent bridge, or the fallback one. + */ + public static AgentBridge connect(final AgentBridge fallback) { + final FeatureFlaggingGateway.Backend backend = FeatureFlaggingGateway.backend(); + return backend == null ? fallback : new JavaAgentBridge(backend); + } + + @Nullable + @Override + public String setting(final String key) { + return this.backend.setting(key); + } + + @Nullable + @Override + public ConfigurationSource remoteConfiguration() { + return this.backend.isRemoteConfigAvailable() + ? new AgentConfigurationSource(this.backend) + : null; + } + + @Nullable + @Override + public EventTransport eventProxy() { + return this.backend.isEventProxyAvailable() ? new AgentEventProxy(this.backend) : null; + } + + @Override + public HealthMetrics healthMetrics() { + return this.healthMetrics; + } + + @Override + public SpanEnricher spanEnricher() { + return this.spanEnricher; + } + + @Override + public ShutdownSource shutdown() { + // TODO Flush the SDK through the agent shutdown. Until then, the SDK uses a JVM shutdown hook. + return null; + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationDisabledForkedTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationDisabledForkedTest.java new file mode 100644 index 00000000000..4a825dfd686 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationDisabledForkedTest.java @@ -0,0 +1,26 @@ +package datadog.trace.instrumentation.openfeature; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; + +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridges; +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import datadog.trace.test.junit.utils.config.WithConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +@WithConfig(key = "feature.flags.enabled", value = "false") +class DDOpenFeatureInstrumentationDisabledForkedTest extends AbstractInstrumentationTest { + @AfterEach + void unregisterBackend() { + FeatureFlaggingGateway.register(null); + } + + @Test + void doesNotInstrumentWhenFeatureFlagsIsDisabled() { + FeatureFlaggingGateway.register(mock(FeatureFlaggingGateway.Backend.class)); + assertSame(AgentBridge.NONE, AgentBridges.detect()); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationTest.java new file mode 100644 index 00000000000..61470453cd9 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.69/src/test/java/datadog/trace/instrumentation/openfeature/DDOpenFeatureInstrumentationTest.java @@ -0,0 +1,103 @@ +package datadog.trace.instrumentation.openfeature; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridges; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventProxyUnavailableException; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import java.util.function.Consumer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +// Bootstrap types are not allowed in test class signatures, so the backend is a local variable. +class DDOpenFeatureInstrumentationTest extends AbstractInstrumentationTest { + @AfterEach + void unregisterBackend() { + FeatureFlaggingGateway.register(null); + } + + @Test + void detectsTheAgentBridge() { + final FeatureFlaggingGateway.Backend backend = mock(FeatureFlaggingGateway.Backend.class); + FeatureFlaggingGateway.register(backend); + assertInstanceOf(JavaAgentBridge.class, AgentBridges.detect()); + } + + @Test + void keepsTheDefaultBridgeWithoutBackend() { + assertSame(AgentBridge.NONE, AgentBridges.detect()); + } + + @Test + void delegatesSettingsAndHealthMetrics() { + final FeatureFlaggingGateway.Backend backend = mock(FeatureFlaggingGateway.Backend.class); + FeatureFlaggingGateway.register(backend); + when(backend.setting("site")).thenReturn("datadoghq.eu"); + final AgentBridge bridge = AgentBridges.detect(); + + assertEquals("datadoghq.eu", bridge.setting("site")); + bridge.healthMetrics().count("metric", 2, "reason"); + verify(backend).count("metric", 2, "reason"); + } + + @Test + void exposesRemoteConfigurationOnlyWhenAvailable() throws Exception { + final FeatureFlaggingGateway.Backend backend = mock(FeatureFlaggingGateway.Backend.class); + FeatureFlaggingGateway.register(backend); + final AgentBridge bridge = AgentBridges.detect(); + assertNull(bridge.remoteConfiguration()); + + when(backend.isRemoteConfigAvailable()).thenReturn(true); + final AutoCloseable subscription = mock(AutoCloseable.class); + when(backend.subscribeRemoteConfig(any())).thenReturn(subscription); + final ConfigurationSource source = bridge.remoteConfiguration(); + final Consumer listener = content -> {}; + + assertSame(subscription, source.subscribe(listener)); + verify(backend).subscribeRemoteConfig(listener); + } + + @Test + void postsThroughTheEventProxyOnlyWhenAvailable() throws Exception { + final FeatureFlaggingGateway.Backend backend = mock(FeatureFlaggingGateway.Backend.class); + FeatureFlaggingGateway.register(backend); + final AgentBridge bridge = AgentBridges.detect(); + assertNull(bridge.eventProxy()); + + when(backend.isEventProxyAvailable()).thenReturn(true); + final EventTransport proxy = bridge.eventProxy(); + final byte[] payload = "{}".getBytes(UTF_8); + when(backend.postEvent("exposures", payload)).thenReturn(true); + proxy.post("exposures", payload); + verify(backend).postEvent("exposures", payload); + + when(backend.postEvent("exposures", payload)).thenReturn(false); + assertThrows(EventProxyUnavailableException.class, () -> proxy.post("exposures", payload)); + } + + @Test + void delegatesSpanEnrichment() { + final FeatureFlaggingGateway.Backend backend = mock(FeatureFlaggingGateway.Backend.class); + FeatureFlaggingGateway.register(backend); + final AgentBridge bridge = AgentBridges.detect(); + + bridge.spanEnricher().serialId(42, true, "user"); + bridge.spanEnricher().runtimeDefault("flag", "default"); + + verify(backend).enrichSerialId(42, true, "user"); + verify(backend).enrichRuntimeDefault("flag", "default"); + } +} diff --git a/dd-openfeature/build.gradle.kts b/dd-openfeature/build.gradle.kts index 3aedba8be93..2401aa4491f 100644 --- a/dd-openfeature/build.gradle.kts +++ b/dd-openfeature/build.gradle.kts @@ -1,5 +1,6 @@ import datadog.gradle.configureCompiler import datadog.gradle.plugin.testJvmConstraints.TestJvmConstraintsExtension +import me.champeau.gradle.japicmp.JapicmpTask plugins { `java-library` @@ -43,3 +44,30 @@ jmh { tasks.withType().configureEach { configureCompiler(JavaVersion.VERSION_11) } + +// The agent bridge SPI versions, with the first SDK release shipping each of them. A released +// version is frozen (only default methods can be added): check it against that release. +// TODO Set the v1 baseline once the first SDK release with the agent bridge is published. +val bridgeSpiBaselines = mapOf( + "v1" to null, +) +bridgeSpiBaselines.forEach { (spi, baseline) -> + if (baseline != null) { + val baselineSdk = "com.datadoghq:dd-openfeature:$baseline" + val check = tasks.register("japicmpBridge${spi.uppercase()}") { + description = "Checks the agent bridge SPI $spi stays compatible with dd-openfeature $baseline." + oldArchives.from(configurations.detachedConfiguration(dependencies.create("$baselineSdk@jar"))) + oldClasspath.from(configurations.detachedConfiguration(dependencies.create(baselineSdk))) + newArchives.from(tasks.named("jar")) + newClasspath.from(configurations.named("runtimeClasspath")) + packageIncludes.add("com.datadog.openfeature.internal.bridge.$spi") + // The agent implements the SPI: an abstract method added to an interface breaks it. + failOnModification.set(true) + failOnSourceIncompatibility.set(true) + txtOutputFile.set(layout.buildDirectory.file("reports/japicmp/bridge-$spi.txt")) + } + tasks.named("check") { + dependsOn(check) + } + } +} diff --git a/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationEnqueueContentionBenchmark.java b/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationEnqueueContentionBenchmark.java index 7ca26289a3e..55fde9906c6 100644 --- a/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationEnqueueContentionBenchmark.java +++ b/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationEnqueueContentionBenchmark.java @@ -4,7 +4,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.util.HashMap; import java.util.Map; diff --git a/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationHotPathBenchmark.java b/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationHotPathBenchmark.java index d5f756d6d1a..197b15c8ca4 100644 --- a/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationHotPathBenchmark.java +++ b/dd-openfeature/src/jmh/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationHotPathBenchmark.java @@ -4,8 +4,8 @@ import static java.util.concurrent.TimeUnit.SECONDS; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import java.util.HashMap; import java.util.Map; import org.openjdk.jmh.annotations.Benchmark; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java b/dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java index 384fadb9585..8c6ced76c65 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java @@ -1,7 +1,7 @@ package com.datadog.openfeature; import com.datadog.openfeature.internal.FeatureFlagsRuntime; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.Settings; import com.datadog.openfeature.internal.evaluation.ConfigurationStore; import com.datadog.openfeature.internal.evaluation.FlagEvaluator; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/Provider.java b/dd-openfeature/src/main/java/com/datadog/openfeature/Provider.java index b8b72493c9d..e3ac093b7ad 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/Provider.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/Provider.java @@ -4,8 +4,8 @@ import com.datadog.openfeature.internal.FeatureFlagsRuntime; import com.datadog.openfeature.internal.FeatureFlagsRuntime.RemoteConfigurationUnavailableException; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.AgentBridges; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridges; import com.datadog.openfeature.internal.config.Settings; import com.datadog.openfeature.internal.flagevaluation.FlagEvaluationWriter; import dev.openfeature.sdk.ErrorCode; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/SpanEnrichmentHook.java b/dd-openfeature/src/main/java/com/datadog/openfeature/SpanEnrichmentHook.java index c02f3705c8c..13dc63ac9fd 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/SpanEnrichmentHook.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/SpanEnrichmentHook.java @@ -1,6 +1,6 @@ package com.datadog.openfeature; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import com.datadog.openfeature.internal.evaluation.MetadataKeys; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FlagEvaluationDetails; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/FeatureFlagsRuntime.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/FeatureFlagsRuntime.java index 4ff8fecadac..393b0ef300d 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/FeatureFlagsRuntime.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/FeatureFlagsRuntime.java @@ -2,10 +2,10 @@ import static com.datadog.openfeature.internal.config.Settings.CONFIGURATION_SOURCE_REMOTE_CONFIG; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; import com.datadog.openfeature.internal.config.Settings; import com.datadog.openfeature.internal.delivery.CdnConfigurationSource; import com.datadog.openfeature.internal.delivery.DirectIntakeTransport; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/RuntimeServices.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/RuntimeServices.java index 8227e19d851..20af59e7674 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/RuntimeServices.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/RuntimeServices.java @@ -1,6 +1,6 @@ package com.datadog.openfeature.internal; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import javax.annotation.Nullable; /** Thread creation and health metrics shared by the runtime components. */ diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/package-info.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/package-info.java deleted file mode 100644 index f4d9dead3c4..00000000000 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Extension points the Datadog Java agent fills in through instrumentation. - * - *

These types are not customer API. They form the contract between this SDK and the agent - * instrumentation, which can run a different version than the SDK, so they must evolve in a - * backward compatible way. - */ -package com.datadog.openfeature.internal.bridge; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridge.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridge.java similarity index 96% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridge.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridge.java index 342c2c2bc8c..22531167beb 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridge.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridge.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import javax.annotation.Nullable; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridges.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridges.java similarity index 91% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridges.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridges.java index f11022880ae..3580094b2a2 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/AgentBridges.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/AgentBridges.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; /** * Resolves the {@link AgentBridge} to use. Kept apart from {@link AgentBridge}: the agent injects diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ConfigurationSource.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ConfigurationSource.java similarity index 89% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ConfigurationSource.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ConfigurationSource.java index 263938ee973..5cda6a93009 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ConfigurationSource.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ConfigurationSource.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import java.util.function.Consumer; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventProxyUnavailableException.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventProxyUnavailableException.java similarity index 87% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventProxyUnavailableException.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventProxyUnavailableException.java index b141a2fce00..209ffe3142a 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventProxyUnavailableException.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventProxyUnavailableException.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import java.io.IOException; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventTransport.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventTransport.java similarity index 89% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventTransport.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventTransport.java index 6f2eb3aca6e..a429099eb77 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/EventTransport.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/EventTransport.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import java.io.IOException; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/HealthMetrics.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/HealthMetrics.java similarity index 89% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/HealthMetrics.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/HealthMetrics.java index 2b1c247cb91..55d79c760c5 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/HealthMetrics.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/HealthMetrics.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import javax.annotation.Nullable; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/NoopAgentBridge.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/NoopAgentBridge.java similarity index 92% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/NoopAgentBridge.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/NoopAgentBridge.java index 0a8ff1709ae..bd684b8ef68 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/NoopAgentBridge.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/NoopAgentBridge.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import javax.annotation.Nullable; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ShutdownSource.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ShutdownSource.java similarity index 86% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ShutdownSource.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ShutdownSource.java index 145bf413b7b..8616003b7f5 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/ShutdownSource.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/ShutdownSource.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; /** A source of application shutdown notifications. */ public interface ShutdownSource { diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/SpanEnricher.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/SpanEnricher.java similarity index 95% rename from dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/SpanEnricher.java rename to dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/SpanEnricher.java index 768481bb6e8..3b1b1c6cd29 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/SpanEnricher.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/SpanEnricher.java @@ -1,4 +1,4 @@ -package com.datadog.openfeature.internal.bridge; +package com.datadog.openfeature.internal.bridge.v1; import javax.annotation.Nullable; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/package-info.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/package-info.java new file mode 100644 index 00000000000..f54d91769bd --- /dev/null +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/bridge/v1/package-info.java @@ -0,0 +1,9 @@ +/** + * Extension points the Datadog Java agent fills in through instrumentation. + * + *

These types are not customer API. They form the contract between this SDK and the agent + * instrumentation, which can run a different version than the SDK. Once released, this version of + * the contract is frozen: only {@code default} methods can be added. A breaking change goes to a + * new {@code bridge.v2} package, and the {@code japicmpBridge} checks enforce it. + */ +package com.datadog.openfeature.internal.bridge.v1; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/config/Settings.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/config/Settings.java index 1213c5d2ca4..b9dc8a63c0d 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/config/Settings.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/config/Settings.java @@ -1,6 +1,6 @@ package com.datadog.openfeature.internal.config; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.util.Locale; import java.util.function.UnaryOperator; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/DirectIntakeTransport.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/DirectIntakeTransport.java index bd2c0171a6b..509f24fb699 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/DirectIntakeTransport.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/DirectIntakeTransport.java @@ -3,7 +3,7 @@ import static java.net.http.HttpClient.Redirect.NEVER; import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; -import com.datadog.openfeature.internal.bridge.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; import com.datadog.openfeature.internal.config.Settings; import java.io.IOException; import java.net.URI; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/FallbackTransport.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/FallbackTransport.java index 3c4ca077f0b..49ae9f3e7c6 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/FallbackTransport.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/FallbackTransport.java @@ -1,7 +1,7 @@ package com.datadog.openfeature.internal.delivery; -import com.datadog.openfeature.internal.bridge.EventProxyUnavailableException; -import com.datadog.openfeature.internal.bridge.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.EventProxyUnavailableException; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/RemoteConfigurationService.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/RemoteConfigurationService.java index 8d1a4c6434a..63cae4527cb 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/RemoteConfigurationService.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/delivery/RemoteConfigurationService.java @@ -1,7 +1,7 @@ package com.datadog.openfeature.internal.delivery; import com.datadog.openfeature.internal.ConfigurationService; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; import com.datadog.openfeature.internal.ufc.ServerConfiguration; import com.datadog.openfeature.internal.ufc.UniversalFlagConfigParser; import java.io.IOException; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/exposure/ExposurePipeline.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/exposure/ExposurePipeline.java index 9577b1df211..fb0acac0989 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/exposure/ExposurePipeline.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/exposure/ExposurePipeline.java @@ -5,7 +5,7 @@ import com.datadog.openfeature.internal.EventPipeline; import com.datadog.openfeature.internal.EventQueue; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationPipeline.java b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationPipeline.java index e3daf400dae..915ce31f61b 100644 --- a/dd-openfeature/src/main/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationPipeline.java +++ b/dd-openfeature/src/main/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationPipeline.java @@ -5,7 +5,7 @@ import com.datadog.openfeature.internal.EventPipeline; import com.datadog.openfeature.internal.EventQueue; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.ArrayList; import java.util.List; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/CanonicalFixtureTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/CanonicalFixtureTest.java index b2496d0f3dd..9dd40371799 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/CanonicalFixtureTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/CanonicalFixtureTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import com.datadog.openfeature.internal.JsonReading; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.ufc.UniversalFlagConfigParser; import dev.openfeature.sdk.ErrorCode; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorSpanEnrichmentTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorSpanEnrichmentTest.java index 4f8bbca6e83..cd3717de70e 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorSpanEnrichmentTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorSpanEnrichmentTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.ufc.Allocation; import com.datadog.openfeature.internal.ufc.Flag; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorTest.java index 7998a071a8c..f38c0eaf570 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/DDEvaluatorTest.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.when; import com.datadog.openfeature.internal.LocalHttpServer; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.Settings; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.exposure.ExposureDeduplicationCache; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/DatadogExposureLoggingTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/DatadogExposureLoggingTest.java index 7f75c8ab611..ddc32258e95 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/DatadogExposureLoggingTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/DatadogExposureLoggingTest.java @@ -10,12 +10,12 @@ import com.datadog.openfeature.Provider.Options; import com.datadog.openfeature.internal.LocalHttpServer; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; -import com.datadog.openfeature.internal.bridge.ShutdownSource; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.exposure.ExposureEvent; import com.datadog.openfeature.internal.ufc.Allocation; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/EvaluationTelemetryOptOutTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/EvaluationTelemetryOptOutTest.java index d5e3ab218c0..b85b8dff282 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/EvaluationTelemetryOptOutTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/EvaluationTelemetryOptOutTest.java @@ -20,9 +20,9 @@ import com.datadog.openfeature.internal.ConfigurationService; import com.datadog.openfeature.internal.FeatureFlagsRuntime; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.HealthMetrics; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.exposure.ExposureDeduplicationCache; import com.datadog.openfeature.internal.exposure.ExposureEvent; @@ -82,7 +82,7 @@ * {@link ExposureHook} evaluations. * *

The provider reads its settings from the real system properties and environment through {@link - * com.datadog.openfeature.internal.bridge.AgentBridges#detect()}, so the configuration scenarios + * com.datadog.openfeature.internal.bridge.v1.AgentBridges#detect()}, so the configuration scenarios * use the system property; the environment fallback is covered by {@code SettingsTest}. */ class EvaluationTelemetryOptOutTest { diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureLoggingHookTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureLoggingHookTest.java index 70d2c6f468a..e705bb0ffef 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureLoggingHookTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureLoggingHookTest.java @@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.datadog.openfeature.Provider.Options; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.exposure.ExposureEvent; import com.datadog.openfeature.internal.ufc.Allocation; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureTestEvaluator.java b/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureTestEvaluator.java index db8e81a2c08..9e95c00f9a5 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureTestEvaluator.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/ExposureTestEvaluator.java @@ -2,7 +2,7 @@ import static org.mockito.Mockito.mock; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.exposure.ExposureDeduplicationCache; import com.datadog.openfeature.internal.exposure.ExposureEvent; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/ProviderTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/ProviderTest.java index 97325594548..5e8b9c47ce3 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/ProviderTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/ProviderTest.java @@ -23,12 +23,12 @@ import com.datadog.openfeature.Provider.Options; import com.datadog.openfeature.internal.JsonReading; import com.datadog.openfeature.internal.LocalHttpServer; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; -import com.datadog.openfeature.internal.bridge.ShutdownSource; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.ufc.Allocation; import com.datadog.openfeature.internal.ufc.Flag; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/SpanEnrichmentHookTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/SpanEnrichmentHookTest.java index 8267b28993c..e8efe65ef95 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/SpanEnrichmentHookTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/SpanEnrichmentHookTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.FlagValueType; import dev.openfeature.sdk.Hook; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/FeatureFlagsRuntimeTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/FeatureFlagsRuntimeTest.java index 995174b8fa4..248edec0485 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/FeatureFlagsRuntimeTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/FeatureFlagsRuntimeTest.java @@ -13,12 +13,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; -import com.datadog.openfeature.internal.bridge.ShutdownSource; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.flagevaluation.FlagEvalEvent; import com.datadog.openfeature.internal.ufc.ServerConfiguration; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/SettingsTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/SettingsTest.java index 80c3a9258b3..9e6675a1edc 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/SettingsTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/SettingsTest.java @@ -5,12 +5,12 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.datadog.openfeature.internal.bridge.AgentBridge; -import com.datadog.openfeature.internal.bridge.ConfigurationSource; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; -import com.datadog.openfeature.internal.bridge.ShutdownSource; -import com.datadog.openfeature.internal.bridge.SpanEnricher; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.ConfigurationSource; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.ShutdownSource; +import com.datadog.openfeature.internal.bridge.v1.SpanEnricher; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/TestSettings.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/TestSettings.java index 6acc0792138..45f5dfb00dc 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/TestSettings.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/config/TestSettings.java @@ -1,6 +1,6 @@ package com.datadog.openfeature.internal.config; -import com.datadog.openfeature.internal.bridge.AgentBridge; +import com.datadog.openfeature.internal.bridge.v1.AgentBridge; import java.util.HashMap; import java.util.Map; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/CdnConfigurationSourceTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/CdnConfigurationSourceTest.java index 7ddcbaba216..9dc8168dd5c 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/CdnConfigurationSourceTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/CdnConfigurationSourceTest.java @@ -17,7 +17,7 @@ import com.datadog.openfeature.internal.LocalHttpServer; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import com.datadog.openfeature.internal.config.Settings; import com.datadog.openfeature.internal.config.TestSettings; import com.datadog.openfeature.internal.ufc.ServerConfiguration; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/FallbackTransportTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/FallbackTransportTest.java index 585236831f4..d125e60cadd 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/FallbackTransportTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/delivery/FallbackTransportTest.java @@ -8,8 +8,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import com.datadog.openfeature.internal.bridge.EventProxyUnavailableException; -import com.datadog.openfeature.internal.bridge.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.EventProxyUnavailableException; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; import java.io.IOException; import org.junit.jupiter.api.Test; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/exposure/ExposurePipelineTest.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/exposure/ExposurePipelineTest.java index 38574b51d44..1324100acdb 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/exposure/ExposurePipelineTest.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/exposure/ExposurePipelineTest.java @@ -11,8 +11,8 @@ import com.datadog.openfeature.internal.JsonReading; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; diff --git a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationTestSupport.java b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationTestSupport.java index 1dd1e51d911..68ec703e81f 100644 --- a/dd-openfeature/src/test/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationTestSupport.java +++ b/dd-openfeature/src/test/java/com/datadog/openfeature/internal/flagevaluation/FlagEvaluationTestSupport.java @@ -7,8 +7,8 @@ import com.datadog.openfeature.internal.JsonReading; import com.datadog.openfeature.internal.RuntimeServices; -import com.datadog.openfeature.internal.bridge.EventTransport; -import com.datadog.openfeature.internal.bridge.HealthMetrics; +import com.datadog.openfeature.internal.bridge.v1.EventTransport; +import com.datadog.openfeature.internal.bridge.v1.HealthMetrics; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/metadata/agent-jar-checks.properties b/metadata/agent-jar-checks.properties index 82bc8070731..27b576ea1c4 100644 --- a/metadata/agent-jar-checks.properties +++ b/metadata/agent-jar-checks.properties @@ -128,6 +128,7 @@ expected.integrations = IastInstrumentation,\ ognl,\ okhttp,\ openai-java,\ + openfeature,\ opensearch,\ opentelemetry-annotations,\ opentelemetry-beta,\ diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index b9dc242c780..e0c919b4414 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -8676,6 +8676,14 @@ "aliases": ["DD_TRACE_INTEGRATION_OPENAI_JAVA_ENABLED", "DD_INTEGRATION_OPENAI_JAVA_ENABLED"] } ], + "DD_TRACE_OPENFEATURE_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "true", + "aliases": ["DD_TRACE_INTEGRATION_OPENFEATURE_ENABLED", "DD_INTEGRATION_OPENFEATURE_ENABLED"] + } + ], "DD_TRACE_OPENSEARCH_ANALYTICS_ENABLED": [ { "version": "A", diff --git a/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java b/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java index 2a823bd32ef..191841dfabd 100644 --- a/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java +++ b/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java @@ -3,10 +3,12 @@ import datadog.trace.api.featureflag.exposure.ExposureEvent; import datadog.trace.api.featureflag.flagevaluation.FlagEvaluationWriter; import datadog.trace.api.featureflag.ufc.v1.ServerConfiguration; +import java.io.IOException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import javax.annotation.Nullable; public abstract class FeatureFlaggingGateway { @@ -39,9 +41,27 @@ public interface SpanEnrichmentListener extends Consumer {} new AtomicReference<>(); private static volatile boolean flagEvalEnqueueEnabled = true; + private static volatile Backend backend; private FeatureFlaggingGateway() {} + /** + * Registers the agent backend. + * + * @param registered the backend, or {@code null} to unregister it. + */ + public static void register(@Nullable final Backend registered) { + backend = registered; + } + + /** + * @return the agent backend, or {@code null} if Feature Flags is not started. + */ + @Nullable + public static Backend backend() { + return backend; + } + public static void addConfigListener(final ConfigListener listener) { CONFIG_LISTENERS.add(listener); final ServerConfiguration current = CURRENT_CONFIG.get(); @@ -128,4 +148,72 @@ public static void removeSpanEnrichmentListener(final SpanEnrichmentListener lis public static void dispatch(final SpanEnrichmentEvent event) { SPAN_ENRICHMENT_LISTENERS.forEach(listener -> listener.accept(event)); } + + /** The agent services exposed to the Feature Flags SDK. */ + public interface Backend { + /** + * Looks up a setting from the agent configuration. + * + * @param key the setting key, using the {@code dd.} system property notation without prefix. + * @return the setting value, or {@code null} if not set. + */ + @Nullable + String setting(String key); + + /** + * @return whether Remote Configuration is available. + */ + boolean isRemoteConfigAvailable(); + + /** + * Subscribes to the Feature Flags Remote Configuration product. + * + * @param listener the listener receiving raw configuration documents, or {@code null} when the + * configuration is removed. + * @return the handle to close to unsubscribe. + */ + AutoCloseable subscribeRemoteConfig(Consumer listener); + + /** + * @return whether the Datadog Agent event platform proxy is available. + */ + boolean isEventProxyAvailable(); + + /** + * Posts an event payload through the Datadog Agent event platform proxy. + * + * @param route the event platform route. + * @param json the UTF-8 JSON payload. + * @return {@code true} if the payload was delivered, {@code false} if the proxy definitively + * rejected it as unavailable. + * @throws IOException if the payload could not be delivered. + */ + boolean postEvent(String route, byte[] json) throws IOException; + + /** + * Increments a health counter. + * + * @param metric the metric name. + * @param value the increment. + * @param reason the optional {@code reason} tag value. + */ + void count(String metric, long value, @Nullable String reason); + + /** + * Records an evaluation that resolved to a split with a serial id on the local root span. + * + * @param serialId the split serial id. + * @param doLog whether the allocation logs exposures. + * @param targetingKey the optional targeting key. + */ + void enrichSerialId(int serialId, boolean doLog, @Nullable String targetingKey); + + /** + * Records an evaluation that resolved to its runtime default on the local root span. + * + * @param flagKey the flag key. + * @param defaultValue the native default value. + */ + void enrichRuntimeDefault(String flagKey, @Nullable Object defaultValue); + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index b58d64cc1a3..7e83730b139 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -345,6 +345,7 @@ include( ":dd-java-agent:instrumentation:cxf-2.1", ":dd-java-agent:instrumentation:datadog:asm:iast-instrumenter", ":dd-java-agent:instrumentation:datadog:dynamic-instrumentation:span-origin", + ":dd-java-agent:instrumentation:datadog:openfeature:dd-openfeature-1.69", ":dd-java-agent:instrumentation:datadog:profiling:enable-wallclock-profiling", ":dd-java-agent:instrumentation:datadog:profiling:exception-profiling", ":dd-java-agent:instrumentation:datadog:tracing:trace-annotation",