From f34cb203d4ca87a078c5982b6f05d929a27a69a2 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Thu, 26 Mar 2026 11:02:54 +0100 Subject: [PATCH 01/11] Report dd-java-agent itself as a Maven dependency in SCA telemetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate META-INF/maven/com.datadoghq/dd-java-agent/pom.properties at build time so the SCA dependency pipeline (JarReader → DependencyResolver) detects com.datadoghq:dd-java-agent in the APP_DEPENDENCIES_LOADED telemetry events. Add a unit test in DependencyResolverSpecification verifying the resolver correctly parses the generated pom.properties, and extend the AbstractServerSmokeTest tracer-dependency assertion to include com.datadoghq:dd-java-agent so all server smoke tests validate this end-to-end against the real shadow JAR. Co-Authored-By: Claude Sonnet 4.6 --- dd-java-agent/build.gradle | 12 +++++++++++ .../smoketest/AbstractServerSmokeTest.groovy | 2 +- .../DependencyResolverSpecification.groovy | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index a34b233cea9..e422b38bd1b 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -324,6 +324,18 @@ def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) { } sourceSets.main.resources.srcDir(generateAgentJarIndex) +def generatePomProperties = tasks.register('generatePomProperties') { + def outputDir = project.layout.buildDirectory.dir("generated/maven-metadata") + inputs.property("version", project.provider { project.version.toString() }) + outputs.dir(outputDir) + doLast { + def pomPropsFile = outputDir.get().file("META-INF/maven/com.datadoghq/dd-java-agent/pom.properties").asFile + pomPropsFile.parentFile.mkdirs() + pomPropsFile.text = "groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=${project.version}\n" + } +} +sourceSets.main.resources.srcDir(generatePomProperties) + subprojects { Project subProj -> // Don't need javadoc task run for internal projects. subProj.tasks.withType(Javadoc).configureEach { enabled = false } diff --git a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractServerSmokeTest.groovy b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractServerSmokeTest.groovy index 19972aae71a..bb1b90304d4 100644 --- a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractServerSmokeTest.groovy +++ b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractServerSmokeTest.groovy @@ -183,7 +183,7 @@ abstract class AbstractServerSmokeTest extends AbstractSmokeTest { and: 'received tracer dependencies' // Not exhaustive list of tracer dependencies. - Set missingDependencyNames = ['com.github.jnr:jnr-ffi', 'net.bytebuddy:byte-buddy-agent',].toSet() + Set missingDependencyNames = ['com.datadoghq:dd-java-agent', 'com.github.jnr:jnr-ffi', 'net.bytebuddy:byte-buddy-agent',].toSet() missingDependencyNames.removeAll(dependencyNames) || true missingDependencyNames.isEmpty() diff --git a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy index 4437f1a1c65..fa2a8bfba17 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy @@ -95,6 +95,26 @@ class DependencyResolverSpecification extends DepSpecification { ) } + void 'jar with dd-java-agent pom.properties resolves to com.datadoghq:dd-java-agent'() { + given: 'a jar containing META-INF/maven/com.datadoghq/dd-java-agent/pom.properties' + File file = new File(testDir, 'dd-java-agent.jar') + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file)) + ZipEntry e = new ZipEntry('META-INF/maven/com.datadoghq/dd-java-agent/pom.properties') + out.putNextEntry(e) + out.write('groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=1.0.0\n'.getBytes('UTF-8')) + out.closeEntry() + out.close() + + when: + List deps = DependencyResolver.resolve(file.toURI()) + + then: + deps.size() == 1 + deps[0].name == 'com.datadoghq:dd-java-agent' + deps[0].version == '1.0.0' + deps[0].hash == null + } + void 'jar without manifest and no version in filename gets resolved'() { // If no manifest info and no suitable file name - calculate sha1 hash knownJarCheck( From 7f594e98fb8a7b969bf7a26325a6ed7a8851909b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez=20Garc=C3=ADa?= Date: Tue, 7 Apr 2026 14:06:14 +0200 Subject: [PATCH 02/11] Update telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy Co-authored-by: Brice Dutheil --- .../dependency/DependencyResolverSpecification.groovy | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy index fa2a8bfba17..c2b96596cd0 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy @@ -99,11 +99,12 @@ class DependencyResolverSpecification extends DepSpecification { given: 'a jar containing META-INF/maven/com.datadoghq/dd-java-agent/pom.properties' File file = new File(testDir, 'dd-java-agent.jar') ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file)) - ZipEntry e = new ZipEntry('META-INF/maven/com.datadoghq/dd-java-agent/pom.properties') - out.putNextEntry(e) - out.write('groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=1.0.0\n'.getBytes('UTF-8')) - out.closeEntry() - out.close() + ZipEntry e = new ZipEntry('META-INF/maven/com.datadoghq/dd-java-agent/pom.properties').with { + putNextEntry(e) + write('groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=1.0.0\n'.getBytes('UTF-8')) + closeEntry() + close() + } when: List deps = DependencyResolver.resolve(file.toURI()) From 2e338ddef98828ce2c66380a07392c979aaa449a Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:08:13 +0200 Subject: [PATCH 03/11] Apply reviewer suggestions - Use built-in Gradle WriteProperties task instead of custom doLast action, which handles inputs/outputs/caching natively - Fix Groovy with{} usage in test: apply it on ZipOutputStream (not ZipEntry as incorrectly suggested in the upstream commit) --- dd-java-agent/build.gradle | 20 +++++++++---------- .../DependencyResolverSpecification.groovy | 11 +++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index e422b38bd1b..a77456888a8 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -324,17 +324,17 @@ def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) { } sourceSets.main.resources.srcDir(generateAgentJarIndex) -def generatePomProperties = tasks.register('generatePomProperties') { - def outputDir = project.layout.buildDirectory.dir("generated/maven-metadata") - inputs.property("version", project.provider { project.version.toString() }) - outputs.dir(outputDir) - doLast { - def pomPropsFile = outputDir.get().file("META-INF/maven/com.datadoghq/dd-java-agent/pom.properties").asFile - pomPropsFile.parentFile.mkdirs() - pomPropsFile.text = "groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=${project.version}\n" - } +def pomPropertiesOutputDir = project.layout.buildDirectory.dir("generated/maven-metadata") +def generatePomProperties = tasks.register('generatePomProperties', WriteProperties) { + destinationFile = pomPropertiesOutputDir.map { it.file("META-INF/maven/com.datadoghq/dd-java-agent/pom.properties") } + property("groupId", "com.datadoghq") + property("artifactId", "dd-java-agent") + property("version", project.providers.provider { project.version.toString() }) +} +sourceSets.main.resources.srcDir(pomPropertiesOutputDir) +tasks.named("processResources") { + dependsOn(generatePomProperties) } -sourceSets.main.resources.srcDir(generatePomProperties) subprojects { Project subProj -> // Don't need javadoc task run for internal projects. diff --git a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy index c2b96596cd0..6e8096bc57a 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/dependency/DependencyResolverSpecification.groovy @@ -98,12 +98,11 @@ class DependencyResolverSpecification extends DepSpecification { void 'jar with dd-java-agent pom.properties resolves to com.datadoghq:dd-java-agent'() { given: 'a jar containing META-INF/maven/com.datadoghq/dd-java-agent/pom.properties' File file = new File(testDir, 'dd-java-agent.jar') - ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file)) - ZipEntry e = new ZipEntry('META-INF/maven/com.datadoghq/dd-java-agent/pom.properties').with { - putNextEntry(e) - write('groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=1.0.0\n'.getBytes('UTF-8')) - closeEntry() - close() + new ZipOutputStream(new FileOutputStream(file)).with { + putNextEntry(new ZipEntry('META-INF/maven/com.datadoghq/dd-java-agent/pom.properties')) + write('groupId=com.datadoghq\nartifactId=dd-java-agent\nversion=1.0.0\n'.getBytes('UTF-8')) + closeEntry() + close() } when: From bee1fd1dff5e19be441d97b339b3c9d9a1538869 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:15:41 +0200 Subject: [PATCH 04/11] Use WriteProperties task and wire srcDir via task provider Use the built-in Gradle WriteProperties task as suggested by bric3. Instead of splitting the output directory into a separate variable and adding an explicit dependsOn (which bric3 did not suggest), derive the srcDir provider from the task itself via generatePomProperties.map{...}. This way Gradle infers the processResources -> generatePomProperties dependency automatically, without extra boilerplate. --- dd-java-agent/build.gradle | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index a77456888a8..fb51103c90c 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -324,17 +324,15 @@ def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) { } sourceSets.main.resources.srcDir(generateAgentJarIndex) -def pomPropertiesOutputDir = project.layout.buildDirectory.dir("generated/maven-metadata") def generatePomProperties = tasks.register('generatePomProperties', WriteProperties) { - destinationFile = pomPropertiesOutputDir.map { it.file("META-INF/maven/com.datadoghq/dd-java-agent/pom.properties") } + destinationFile = project.layout.buildDirectory.file("generated/maven-metadata/META-INF/maven/com.datadoghq/dd-java-agent/pom.properties") property("groupId", "com.datadoghq") property("artifactId", "dd-java-agent") property("version", project.providers.provider { project.version.toString() }) } -sourceSets.main.resources.srcDir(pomPropertiesOutputDir) -tasks.named("processResources") { - dependsOn(generatePomProperties) -} +sourceSets.main.resources.srcDir( + generatePomProperties.map { project.layout.buildDirectory.dir("generated/maven-metadata").get() } +) subprojects { Project subProj -> // Don't need javadoc task run for internal projects. From e05f35944b84cd88e9c64d2fd9dd63ea962a7fcb Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:23:01 +0200 Subject: [PATCH 05/11] spotless --- dd-java-agent/build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index fb51103c90c..c320150333b 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -331,8 +331,10 @@ def generatePomProperties = tasks.register('generatePomProperties', WritePropert property("version", project.providers.provider { project.version.toString() }) } sourceSets.main.resources.srcDir( - generatePomProperties.map { project.layout.buildDirectory.dir("generated/maven-metadata").get() } -) + generatePomProperties.map { + project.layout.buildDirectory.dir("generated/maven-metadata").get() + } + ) subprojects { Project subProj -> // Don't need javadoc task run for internal projects. From 71c67d5503849d8291014b323ea41a5b95e89551 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:49:13 +0200 Subject: [PATCH 06/11] Follow existing pattern for generated resources Follow the same pattern as includedAgentDir/includedJarFileTree: - declare pomPropertiesDir and pomPropertiesFileTree near the top - add dependsOn(pomPropertiesFileTree) to processResources - declare main.resources.srcDir(pomPropertiesDir) in the sourceSets block - register generatePomProperties (WriteProperties) near generateAgentJarIndex - wire pomPropertiesFileTree.builtBy(generatePomProperties) This avoids the need for a task-provider-mapped srcDir since WriteProperties outputs a single file (not a directory), so the fileTree + builtBy approach is the correct way to express the dependency. --- dd-java-agent/build.gradle | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index c320150333b..67e503e1664 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -19,8 +19,12 @@ configurations { def includedAgentDir = project.layout.buildDirectory.dir("generated/included") def includedJarFileTree = fileTree(includedAgentDir) +def pomPropertiesDir = project.layout.buildDirectory.dir("generated/maven-metadata") +def pomPropertiesFileTree = fileTree(pomPropertiesDir) + tasks.named("processResources") { dependsOn(includedJarFileTree) + dependsOn(pomPropertiesFileTree) } sourceSets { @@ -34,6 +38,7 @@ sourceSets { java.srcDirs "${project.projectDir}/src/main/java11" } main.resources.srcDir(includedAgentDir) + main.resources.srcDir(pomPropertiesDir) } def java6CompileTask = tasks.named("compileMain_java6Java") { @@ -325,16 +330,12 @@ def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) { sourceSets.main.resources.srcDir(generateAgentJarIndex) def generatePomProperties = tasks.register('generatePomProperties', WriteProperties) { - destinationFile = project.layout.buildDirectory.file("generated/maven-metadata/META-INF/maven/com.datadoghq/dd-java-agent/pom.properties") + destinationFile = pomPropertiesDir.map { it.file("META-INF/maven/com.datadoghq/dd-java-agent/pom.properties") } property("groupId", "com.datadoghq") property("artifactId", "dd-java-agent") property("version", project.providers.provider { project.version.toString() }) } -sourceSets.main.resources.srcDir( - generatePomProperties.map { - project.layout.buildDirectory.dir("generated/maven-metadata").get() - } - ) +pomPropertiesFileTree.builtBy(generatePomProperties) subprojects { Project subProj -> // Don't need javadoc task run for internal projects. From 9d46af9df3d4a2b14ab51ae21906c961abf8f338 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:53:55 +0200 Subject: [PATCH 07/11] Combine srcDirs as suggested by reviewer Replace two separate srcDir calls with a single srcDirs(includedAgentDir, pomPropertiesDir) as suggested by bric3. --- dd-java-agent/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 67e503e1664..2712a83e485 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -37,8 +37,7 @@ sourceSets { "main_java11" { java.srcDirs "${project.projectDir}/src/main/java11" } - main.resources.srcDir(includedAgentDir) - main.resources.srcDir(pomPropertiesDir) + main.resources.srcDirs(includedAgentDir, pomPropertiesDir) } def java6CompileTask = tasks.named("compileMain_java6Java") { From 7064cc9156397207271b900f3c7ebe6d62db1cab Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 14:57:36 +0200 Subject: [PATCH 08/11] Keep original srcDir and add srcDirs per reviewer diff --- dd-java-agent/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 2712a83e485..e35fc3663b5 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -37,6 +37,7 @@ sourceSets { "main_java11" { java.srcDirs "${project.projectDir}/src/main/java11" } + main.resources.srcDir(includedAgentDir) main.resources.srcDirs(includedAgentDir, pomPropertiesDir) } From 32cc790d05a207f4798b930c23dd565b7e1af397 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 15:11:46 +0200 Subject: [PATCH 09/11] Fix duplicate srcDir causing processResources failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit includedAgentDir was registered twice — once via srcDir and again inside srcDirs. Replace both with a single srcDirs call. --- dd-java-agent/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index e35fc3663b5..2712a83e485 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -37,7 +37,6 @@ sourceSets { "main_java11" { java.srcDirs "${project.projectDir}/src/main/java11" } - main.resources.srcDir(includedAgentDir) main.resources.srcDirs(includedAgentDir, pomPropertiesDir) } From 1dfc56d5db56ccdc1afdfad31760e8a9c559d079 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 15:41:13 +0200 Subject: [PATCH 10/11] Fix implicit dependency on generatePomProperties from sourcesJar sourcesJar includes main.resources srcDirs (including pomPropertiesDir) but had no dependency on generatePomProperties. Add explicit dependsOn(pomPropertiesFileTree) consistent with the processResources wiring. --- dd-java-agent/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 2712a83e485..f05fe7ca779 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -27,6 +27,10 @@ tasks.named("processResources") { dependsOn(pomPropertiesFileTree) } +tasks.named("sourcesJar") { + dependsOn(pomPropertiesFileTree) +} + sourceSets { // The special pre-check must be compiled with Java 6 to detect unsupported // Java versions and prevent issues for users that still using them. From 4c4b3d73308ffc316c30727b1ae5f0317a07fdba Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Tue, 7 Apr 2026 17:05:32 +0200 Subject: [PATCH 11/11] chore: trigger CI