Avoid loading `Config` in `DDSpecification`

Loading `Config` in static section of `DDSpecification` happens before
bootstrap jar has been setup. This in turn causes problems down the
road because some of the classes are later loaded from bootstrap and
confuse things.
This commit is contained in:
Nikolay Martynov 2019-10-10 12:33:49 -04:00
parent cfa05b0410
commit ad79db6183
1 changed files with 21 additions and 45 deletions

View File

@ -6,8 +6,6 @@ import net.bytebuddy.dynamic.ClassFileLocator
import net.bytebuddy.dynamic.Transformer
import spock.lang.Specification
import java.lang.reflect.Modifier
import static net.bytebuddy.description.modifier.FieldManifestation.VOLATILE
import static net.bytebuddy.description.modifier.Ownership.STATIC
import static net.bytebuddy.description.modifier.Visibility.PUBLIC
@ -17,12 +15,6 @@ import static net.bytebuddy.matcher.ElementMatchers.none
abstract class DDSpecification extends Specification {
private static final String CONFIG = "datadog.trace.api.Config"
static class ConfigInstance {
// Wrapped in a static class to lazy load.
static final CONFIG_INSTANCE_FIELD = Class.forName(CONFIG).getDeclaredField("INSTANCE")
static final RUNTIME_ID_FIELD = Class.forName(CONFIG).getDeclaredField("runtimeId")
}
static {
makeConfigInstanceModifiable()
}
@ -36,43 +28,27 @@ abstract class DDSpecification extends Specification {
}
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 bootstrap classes
.type(named(CONFIG))
.transform { builder, typeDescription, classLoader, module ->
builder
.field(named("INSTANCE"))
.transform(Transformer.ForField.withModifiers(PUBLIC, STATIC, VOLATILE))
}
// Making runtimeId modifiable so that it can be preserved when resetting config in tests
.transform { builder, typeDescription, classLoader, module ->
builder
.field(named("runtimeId"))
.transform(Transformer.ForField.withModifiers(PUBLIC, VOLATILE))
}
.installOn(instrumentation)
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 bootstrap classes
.type(named(CONFIG))
.transform { builder, typeDescription, classLoader, module ->
builder
.field(named("INSTANCE"))
.transform(Transformer.ForField.withModifiers(PUBLIC, STATIC, VOLATILE))
}
// Making runtimeId modifiable so that it can be preserved when resetting config in tests
.transform { builder, typeDescription, classLoader, module ->
builder
.field(named("runtimeId"))
.transform(Transformer.ForField.withModifiers(PUBLIC, VOLATILE))
}
.installOn(instrumentation)
isConfigInstanceModifiable = true
final field = ConfigInstance.CONFIG_INSTANCE_FIELD
assert Modifier.isPublic(field.getModifiers())
assert Modifier.isStatic(field.getModifiers())
assert Modifier.isVolatile(field.getModifiers())
assert !Modifier.isFinal(field.getModifiers())
final runtimeIdField = ConfigInstance.RUNTIME_ID_FIELD
assert Modifier.isPublic(runtimeIdField.getModifiers())
assert !Modifier.isStatic(ConfigInstance.RUNTIME_ID_FIELD.getModifiers())
assert Modifier.isVolatile(runtimeIdField.getModifiers())
assert !Modifier.isFinal(runtimeIdField.getModifiers())
// No longer needed (Unless class gets retransformed somehow).
instrumentation.removeTransformer(transformer)
}
}