diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AdviceShader.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AdviceShader.java index 07b4426767d..e3ba70a5c0e 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AdviceShader.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AdviceShader.java @@ -1,5 +1,6 @@ package datadog.trace.agent.tooling; +import static java.lang.Integer.toHexString; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -84,9 +85,10 @@ public byte[] shadeClass(byte[] bytecode) { public String uniqueHelper(String dottedName) { int packageEnd = dottedName.lastIndexOf('.'); if (packageEnd > 0) { + // Hex encoding keeps negative hashes valid in Java package names. return dottedName.substring(0, packageEnd + 1) + "shaded" - + relocations.hashCode() + + toHexString(relocations.hashCode()) + dottedName.substring(packageEnd); } return dottedName; diff --git a/dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/tooling/AdviceShaderTest.java b/dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/tooling/AdviceShaderTest.java new file mode 100644 index 00000000000..b6c21f5b9cc --- /dev/null +++ b/dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/tooling/AdviceShaderTest.java @@ -0,0 +1,62 @@ +package datadog.trace.agent.tooling; + +import static java.util.Collections.singletonMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.tabletest.junit.TableTest; + +class AdviceShaderTest { + private static final String HELPER = "datadog.trace.instrumentation.jms.DatadogMessageListener"; + + @TableTest({ + "scenario | relocation ", + "zero hash | '' ", + "positive hash | a ", + "minimum int hash | min-hash-7052265601" + }) + void helperPackageIsAJavaIdentifierForEveryHashSign(String relocation) { + AdviceShader shader = AdviceShader.with(singletonMap("", relocation)); + + assertValidClassName(shader.uniqueHelper(HELPER)); + } + + @Test + void jakartaJmsHelperNamesAndReferencesMatch() { + InstrumenterModule module = + new InstrumenterModule("test-jakarta-jms") { + @Override + public Map adviceShading() { + return singletonMap("javax", "jakarta"); + } + + @Override + public String[] helperClassNames() { + return new String[] {HELPER}; + } + }; + AdviceShader shader = AdviceShader.with(module); + String shadedHelper = shader.uniqueHelper(HELPER); + + assertTrue(module.adviceShading().hashCode() < 0); + assertValidClassName(shadedHelper); + assertNotEquals(HELPER, shadedHelper); + assertEquals(shadedHelper, shader.shadeClassName(HELPER)); + assertEquals( + "L" + shadedHelper.replace('.', '/') + ";", + shader.shadeTypeDescriptor("L" + HELPER.replace('.', '/') + ";")); + assertEquals("jakarta.jms.MessageListener", shader.shadeClassName("javax.jms.MessageListener")); + } + + private static void assertValidClassName(String className) { + for (String segment : className.split("\\.")) { + assertTrue(Character.isJavaIdentifierStart(segment.charAt(0)), className); + for (int i = 1; i < segment.length(); i++) { + assertTrue(Character.isJavaIdentifierPart(segment.charAt(i)), className); + } + } + } +}