Extract ConfigUtils
MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/BaseDecoratorTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/BaseDecoratorTest.groovy
package datadog.trace.agent.decorator
+
+import datadog.trace.agent.test.utils.ConfigUtils
import datadog.trace.api.DDTags
import io.opentracing.Scope
import io.opentracing.Span
@@ -7,11 +9,15 @@ import io.opentracing.tag.Tags
import spock.lang.Shared
import spock.lang.Specification
-import static datadog.trace.agent.test.utils.TraceUtils.withSystemProperty
+import static datadog.trace.agent.test.utils.ConfigUtils.withSystemProperty
import static io.opentracing.log.Fields.ERROR_OBJECT
class BaseDecoratorTest extends Specification {
+ static {
+ ConfigUtils.makeConfigInstanceModifiable()
+ }
+
@Shared
def decorator = newDecorator()
@@ -268,10 +274,12 @@ class BaseDecoratorTest extends Specification {
}
class SomeInnerClass implements Runnable {
- void run() {}
+ void run() {
+ }
}
static class SomeNestedClass implements Runnable {
- void run() {}
+ void run() {
+ }
}
}

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy
@@ -6,7 +6,7 @@ import io.opentracing.Span
import io.opentracing.tag.Tags
import spock.lang.Shared
-import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
+import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride
class HttpClientDecoratorTest extends ClientDecoratorTest {

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpServerDecoratorTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpServerDecoratorTest.groovy
@@ -4,7 +4,7 @@ import datadog.trace.api.Config
import io.opentracing.Span
import io.opentracing.tag.Tags
-import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
+import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride
class HttpServerDecoratorTest extends ServerDecoratorTest {

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/DefaultInstrumenterTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/DefaultInstrumenterTest.groovy
package datadog.trace.agent.test
+import datadog.trace.agent.test.utils.ConfigUtils
import datadog.trace.agent.tooling.Instrumenter
import net.bytebuddy.agent.builder.AgentBuilder
import net.bytebuddy.description.type.TypeDescription
@@ -10,6 +11,10 @@ import org.junit.contrib.java.lang.system.RestoreSystemProperties
import spock.lang.Specification
class DefaultInstrumenterTest extends Specification {
+ static {
+ ConfigUtils.makeConfigInstanceModifiable()
+ }
+
@Rule
public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties()
@Rule

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy
package datadog.trace.agent.test
-
+import datadog.trace.agent.test.utils.ConfigUtils
import datadog.trace.agent.tooling.AgentInstaller
import datadog.trace.agent.tooling.HelperInjector
import datadog.trace.agent.tooling.Utils
@@ -19,6 +19,9 @@ import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOAD
import static datadog.trace.util.gc.GCUtils.awaitGC
class HelperInjectionTest extends Specification {
+ static {
+ ConfigUtils.makeConfigInstanceModifiable()
+ }
@Timeout(10)
def "helpers injected to non-delegating classloader"() {

MODIFY
dd-java-agent/instrumentation/http-url-connection/src/test/groovy/HttpUrlConnectionTest.groovy
dd-java-agent/instrumentation/http-url-connection/src/test/groovy/HttpUrlConnectionTest.groovy
@@ -8,8 +8,8 @@ import spock.lang.Ignore
import spock.lang.Requires
import sun.net.www.protocol.https.HttpsURLConnectionImpl
+import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
-import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.OPERATION_NAME
class HttpUrlConnectionTest extends HttpClientTest<HttpUrlConnectionDecorator> {

MODIFY
dd-java-agent/instrumentation/http-url-connection/src/test/groovy/UrlConnectionTest.groovy
dd-java-agent/instrumentation/http-url-connection/src/test/groovy/UrlConnectionTest.groovy
@@ -5,9 +5,9 @@ import datadog.trace.instrumentation.http_url_connection.UrlInstrumentation
import io.opentracing.tag.Tags
import io.opentracing.util.GlobalTracer
+import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride
import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
-import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.OPERATION_NAME
class UrlConnectionTest extends AgentTestRunner {

MODIFY
dd-java-agent/instrumentation/trace-annotation/src/test/groovy/ConfiguredTraceAnnotationsTest.groovy
dd-java-agent/instrumentation/trace-annotation/src/test/groovy/ConfiguredTraceAnnotationsTest.groovy
@@ -4,7 +4,7 @@ import dd.test.trace.annotation.SayTracedHello
import java.util.concurrent.Callable
-import static datadog.trace.agent.test.utils.TraceUtils.withSystemProperty
+import static datadog.trace.agent.test.utils.ConfigUtils.withSystemProperty
import static datadog.trace.instrumentation.trace_annotation.TraceAnnotationsInstrumentation.DEFAULT_ANNOTATIONS
class ConfiguredTraceAnnotationsTest extends AgentTestRunner {

MODIFY
dd-java-agent/instrumentation/trace-annotation/src/test/groovy/TraceConfigTest.groovy
dd-java-agent/instrumentation/trace-annotation/src/test/groovy/TraceConfigTest.groovy
@@ -3,7 +3,7 @@ import datadog.trace.instrumentation.trace_annotation.TraceConfigInstrumentation
import java.util.concurrent.Callable
-import static datadog.trace.agent.test.utils.TraceUtils.withSystemProperty
+import static datadog.trace.agent.test.utils.ConfigUtils.withSystemProperty
class TraceConfigTest extends AgentTestRunner {

MODIFY
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.java
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.java
package datadog.trace.agent.test;
-import static net.bytebuddy.matcher.ElementMatchers.named;
-import static net.bytebuddy.matcher.ElementMatchers.none;
-
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import com.google.common.collect.Sets;
@@ -10,10 +7,10 @@ import datadog.opentracing.DDSpan;
import datadog.opentracing.DDTracer;
import datadog.opentracing.PendingTrace;
import datadog.trace.agent.test.asserts.ListWriterAssert;
+import datadog.trace.agent.test.utils.ConfigUtils;
import datadog.trace.agent.test.utils.GlobalTracerUtils;
import datadog.trace.agent.tooling.AgentInstaller;
import datadog.trace.agent.tooling.Instrumenter;
-import datadog.trace.api.Config;
import datadog.trace.api.GlobalTracer;
import datadog.trace.common.writer.ListWriter;
import datadog.trace.common.writer.Writer;
@@ -24,8 +21,6 @@ import groovy.transform.stc.SimpleType;
import io.opentracing.Tracer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
@@ -34,14 +29,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
-import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
-import net.bytebuddy.description.modifier.FieldManifestation;
-import net.bytebuddy.description.modifier.Ownership;
-import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
-import net.bytebuddy.dynamic.Transformer;
import net.bytebuddy.utility.JavaModule;
import org.junit.AfterClass;
import org.junit.Before;
@@ -95,11 +84,7 @@ public abstract class AgentTestRunner extends Specification {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN);
((Logger) LoggerFactory.getLogger("datadog")).setLevel(Level.DEBUG);
- try {
- makeConfigInstanceModifiable();
- } catch (final Exception e) {
- throw new RuntimeException(e);
- }
+ ConfigUtils.makeConfigInstanceModifiable();
TEST_WRITER =
new ListWriter() {
@@ -206,44 +191,6 @@ public abstract class AgentTestRunner extends Specification {
: INSTRUMENTATION_ERROR_COUNT.get() + " Instrumentation errors during test";
}
- private static void makeConfigInstanceModifiable()
- throws ClassNotFoundException, NoSuchFieldException {
- final ResettableClassFileTransformer transformer =
- new AgentBuilder.Default()
- // Config is injected into the bootstrap, so we need to provide a locator.
- .with(
- new AgentBuilder.LocationStrategy.Simple(
- ClassFileLocator.ForClassLoader.ofSystemLoader()))
- .ignore(none()) // Allow transforming boostrap classes
- .type(named("datadog.trace.api.Config"))
- .transform(
- new AgentBuilder.Transformer() {
- @Override
- public DynamicType.Builder<?> transform(
- final DynamicType.Builder<?> builder,
- final TypeDescription typeDescription,
- final ClassLoader classLoader,
- final JavaModule module) {
- // Add transformer to modify INSTANCE field access.
- return builder
- .field(named("INSTANCE"))
- .transform(
- Transformer.ForField.withModifiers(
- Visibility.PUBLIC, Ownership.STATIC, FieldManifestation.VOLATILE));
- }
- })
- .installOn(INSTRUMENTATION);
-
- final Field field = Config.class.getDeclaredField("INSTANCE");
- assert Modifier.isPublic(field.getModifiers());
- assert Modifier.isStatic(field.getModifiers());
- assert Modifier.isVolatile(field.getModifiers());
- assert !Modifier.isFinal(field.getModifiers());
-
- // No longer needed (Unless class gets retransformed somehow).
- INSTRUMENTATION.removeTransformer(transformer);
- }
-
public static void assertTraces(
final int size,
@ClosureParams(

MODIFY
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy
@@ -14,9 +14,9 @@ import spock.lang.Unroll
import java.util.concurrent.ExecutionException
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
+import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride
import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
-import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
import static org.junit.Assume.assumeTrue
abstract class HttpClientTest<T extends HttpClientDecorator> extends AgentTestRunner {

ADD
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/ConfigUtils.groovy
+package datadog.trace.agent.test.utils
+
+import datadog.trace.api.Config
+import lombok.SneakyThrows
+import net.bytebuddy.agent.ByteBuddyAgent
+import net.bytebuddy.agent.builder.AgentBuilder
+import net.bytebuddy.dynamic.ClassFileLocator
+import net.bytebuddy.dynamic.Transformer
+
+import java.lang.reflect.Modifier
+import java.util.concurrent.Callable
+
+import static net.bytebuddy.description.modifier.FieldManifestation.VOLATILE
+import static net.bytebuddy.description.modifier.Ownership.STATIC
+import static net.bytebuddy.description.modifier.Visibility.PUBLIC
+import static net.bytebuddy.matcher.ElementMatchers.named
+import static net.bytebuddy.matcher.ElementMatchers.none
+
+class ConfigUtils {
+ private static class ConfigInstance {
+ // Wrapped in a static class to lazy load.
+ static final FIELD = Config.getDeclaredField("INSTANCE")
+ }
+
+ // TODO: ideally all users of this should switch to using Config object (and withConfigOverride) instead.
+ @Deprecated
+ @SneakyThrows
+ static <T extends Object> Object withSystemProperty(final String name, final String value, final Callable<T> r) {
+ if (value == null) {
+ System.clearProperty(name)
+ } else {
+ System.setProperty(name, value)
+ }
+ try {
+ return r.call()
+ } finally {
+ System.clearProperty(name)
+ }
+ }
+
+ @SneakyThrows
+ synchronized static <T extends Object> Object withConfigOverride(final String name, final String value, final Callable<T> r) {
+ // Ensure the class was retransformed properly in AgentTestRunner.makeConfigInstanceModifiable()
+ assert Modifier.isPublic(ConfigInstance.FIELD.getModifiers())
+ assert Modifier.isStatic(ConfigInstance.FIELD.getModifiers())
+ assert Modifier.isVolatile(ConfigInstance.FIELD.getModifiers())
+ assert !Modifier.isFinal(ConfigInstance.FIELD.getModifiers())
+
+ def existingConfig = Config.get()
+ Properties properties = new Properties()
+ properties.put(name, value)
+ ConfigInstance.FIELD.set(null, new Config(properties, existingConfig))
+ assert Config.get() != existingConfig
+ try {
+ return r.call()
+ } finally {
+ ConfigInstance.FIELD.set(null, existingConfig)
+ }
+ }
+
+ /**
+ * Calling will reset the runtimeId too, so it might cause problems around runtimeId verification.
+ */
+ static void resetConfig() {
+ // Ensure the class was retransformed properly in AgentTestRunner.makeConfigInstanceModifiable()
+ assert Modifier.isPublic(ConfigInstance.FIELD.getModifiers())
+ assert Modifier.isStatic(ConfigInstance.FIELD.getModifiers())
+ assert Modifier.isVolatile(ConfigInstance.FIELD.getModifiers())
+ assert !Modifier.isFinal(ConfigInstance.FIELD.getModifiers())
+
+ ConfigInstance.FIELD.set(null, new Config())
+ }
+
+ static void makeConfigInstanceModifiable() {
+ def instrumentation = ByteBuddyAgent.install()
+ final transformer =
+ new AgentBuilder.Default()
+ .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
+ .with(AgentBuilder.RedefinitionStrategy.Listener.ErrorEscalating.FAIL_FAST)
+ // Config is injected into the bootstrap, so we need to provide a locator.
+ .with(
+ new AgentBuilder.LocationStrategy.Simple(
+ ClassFileLocator.ForClassLoader.ofSystemLoader()))
+ .ignore(none()) // Allow transforming boostrap classes
+ .type(named("datadog.trace.api.Config"))
+ .transform { builder, typeDescription, classLoader, module ->
+ builder
+ .field(named("INSTANCE"))
+ .transform(Transformer.ForField.withModifiers(PUBLIC, STATIC, VOLATILE))
+ }
+ .installOn(instrumentation)
+
+ final field = ConfigInstance.FIELD
+ assert Modifier.isPublic(field.getModifiers())
+ assert Modifier.isStatic(field.getModifiers())
+ assert Modifier.isVolatile(field.getModifiers())
+ assert !Modifier.isFinal(field.getModifiers())
+
+ // No longer needed (Unless class gets retransformed somehow).
+ instrumentation.removeTransformer(transformer)
+ }
+}

MODIFY
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy
package datadog.trace.agent.test.utils
import datadog.trace.agent.decorator.BaseDecorator
-import datadog.trace.api.Config
import datadog.trace.context.TraceScope
import io.opentracing.Scope
import io.opentracing.util.GlobalTracer
import lombok.SneakyThrows
-import java.lang.reflect.Modifier
import java.util.concurrent.Callable
class TraceUtils {
- static final def CONFIG_INSTANCE_FIELD = Config.getDeclaredField("INSTANCE")
private static final BaseDecorator DECORATOR = new BaseDecorator() {
protected String[] instrumentationNames() {
@@ -44,52 +41,4 @@ class TraceUtils {
scope.close()
}
}
-
- // TODO: ideally all users of this should switch to using Config object (and withConfigOverride) instead.
- @SneakyThrows
- static <T extends Object> Object withSystemProperty(final String name, final String value, final Callable<T> r) {
- if (value == null) {
- System.clearProperty(name)
- } else {
- System.setProperty(name, value)
- }
- try {
- return r.call()
- } finally {
- System.clearProperty(name)
- }
- }
-
- @SneakyThrows
- synchronized static <T extends Object> Object withConfigOverride(final String name, final String value, final Callable<T> r) {
- // Ensure the class was retransformed properly in AgentTestRunner.makeConfigInstanceModifiable()
- assert Modifier.isPublic(CONFIG_INSTANCE_FIELD.getModifiers())
- assert Modifier.isStatic(CONFIG_INSTANCE_FIELD.getModifiers())
- assert Modifier.isVolatile(CONFIG_INSTANCE_FIELD.getModifiers())
- assert !Modifier.isFinal(CONFIG_INSTANCE_FIELD.getModifiers())
-
- def existingConfig = Config.get()
- Properties properties = new Properties()
- properties.put(name, value)
- CONFIG_INSTANCE_FIELD.set(null, new Config(properties, existingConfig))
- assert Config.get() != existingConfig
- try {
- return r.call()
- } finally {
- CONFIG_INSTANCE_FIELD.set(null, existingConfig)
- }
- }
-
- /**
- * Calling will reset the runtimeId too, so it might cause problems around runtimeId verification.
- */
- static void resetConfig() {
- // Ensure the class was retransformed properly in AgentTestRunner.makeConfigInstanceModifiable()
- assert Modifier.isPublic(CONFIG_INSTANCE_FIELD.getModifiers())
- assert Modifier.isStatic(CONFIG_INSTANCE_FIELD.getModifiers())
- assert Modifier.isVolatile(CONFIG_INSTANCE_FIELD.getModifiers())
- assert !Modifier.isFinal(CONFIG_INSTANCE_FIELD.getModifiers())
-
- CONFIG_INSTANCE_FIELD.set(null, new Config())
- }
}

MODIFY
dd-java-agent/testing/src/test/groovy/AgentTestRunnerTest.groovy
dd-java-agent/testing/src/test/groovy/AgentTestRunnerTest.groovy
@@ -10,7 +10,7 @@ import spock.lang.Shared
import java.lang.reflect.Field
-import static datadog.trace.agent.test.utils.TraceUtils.resetConfig
+import static datadog.trace.agent.test.utils.ConfigUtils.resetConfig
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
import static datadog.trace.api.Config.TRACE_CLASSES_EXCLUDE

Extract ConfigUtils