Make OSGi instrumentation more generic
This should help it work with felix.
Also reduce log noise.
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ByteBuddyElementMatchers.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ByteBuddyElementMatchers.java
@@ -62,11 +62,15 @@ public class ByteBuddyElementMatchers {
return new SafeMatcher<>(matcher, false, description);
}
- private static TypeDescription safeAsErasure(final TypeDefinition target) {
+ private static TypeDescription safeAsErasure(final TypeDefinition typeDefinition) {
try {
- return target.asErasure();
+ return typeDefinition.asErasure();
} catch (final Exception e) {
- log.debug("Exception trying to get interfaces:", e);
+ log.debug(
+ "{} trying to get erasure for target {}: {}",
+ e.getClass().getSimpleName(),
+ typeDefinition.getTypeName(),
+ e.getMessage());
return null;
}
}
@@ -125,7 +129,11 @@ public class ByteBuddyElementMatchers {
try {
return typeDefinition.getSuperClass();
} catch (final Exception e) {
- log.debug("Exception trying to get next type definition:", e);
+ log.debug(
+ "{} trying to get super class for target {}: {}",
+ e.getClass().getSimpleName(),
+ typeDefinition.getTypeName(),
+ e.getMessage());
return null;
}
}
@@ -167,7 +175,11 @@ public class ByteBuddyElementMatchers {
interfaceTypes.add(interfaceIter.next());
}
} catch (final Exception e) {
- log.debug("Exception trying to get interfaces:", e);
+ log.debug(
+ "{} trying to get interfaces for target {}: {}",
+ e.getClass().getSimpleName(),
+ typeDefinition.getTypeName(),
+ e.getMessage());
}
return interfaceTypes;
}

MODIFY
dd-java-agent/instrumentation/osgi-classloading/osgi-classloading.gradle
dd-java-agent/instrumentation/osgi-classloading/osgi-classloading.gradle
@@ -11,6 +11,7 @@ dependencies {
// core version provided by Eclipse implementation.
//testCompile group: 'org.osgi', name: 'org.osgi.core', version: '4.0.0'
testCompile group: 'org.eclipse.platform', name: 'org.eclipse.osgi', version: '3.13.200'
+ testCompile group: 'org.apache.felix', name: 'org.apache.felix.framework', version: '6.0.2'
testCompile project(':dd-java-agent:testing')
}

MODIFY
dd-java-agent/instrumentation/osgi-classloading/src/main/java/datadog/trace/instrumentation/osgi/OSGIClassloadingInstrumentation.java
dd-java-agent/instrumentation/osgi-classloading/src/main/java/datadog/trace/instrumentation/osgi/OSGIClassloadingInstrumentation.java
package datadog.trace.instrumentation.osgi;
+import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@@ -26,7 +27,9 @@ public final class OSGIClassloadingInstrumentation extends Instrumenter.Default
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
// OSGi Bundle class loads the system property which defines bootstrap classes
- return named("org.osgi.framework.Bundle").or(named("org.eclipse.osgi.launch.EquinoxFactory"));
+ return named("org.osgi.framework.Bundle")
+ // OSGi FrameworkFactory can ignore/override the system property
+ .or(safeHasSuperType(named("org.osgi.framework.launch.FrameworkFactory")));
}
@Override
@@ -52,14 +55,14 @@ public final class OSGIClassloadingInstrumentation extends Instrumenter.Default
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod().and(isPublic()).and(named("newFramework")).and(takesArgument(0, Map.class)),
- EquinoxFactoryAdvice.class.getName());
+ FrameworkFactoryAdvice.class.getName());
}
/**
- * Sometimes OSGi doesn't read configuration from system properties. Handle this case for {@code
- * EquinoxFactory}.
+ * FrameworkFactory implementations receive the expected config via a map rather than the modified
+ * system property. We must modify that map before being passed along to the framework.
*/
- public static class EquinoxFactoryAdvice {
+ public static class FrameworkFactoryAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(@Advice.Argument(0) final Map<String, String> configuration) {
if (configuration != null) {

MODIFY
dd-java-agent/instrumentation/osgi-classloading/src/test/groovy/OSGIClassloadingTest.groovy
dd-java-agent/instrumentation/osgi-classloading/src/test/groovy/OSGIClassloadingTest.groovy
@@ -18,15 +18,19 @@ class OSGIClassloadingTest extends AgentTestRunner {
System.getProperty("org.osgi.framework.bootdelegation") == "io.opentracing.*,io.opentracing,datadog.slf4j.*,datadog.slf4j,datadog.trace.bootstrap.*,datadog.trace.bootstrap,datadog.trace.api.*,datadog.trace.api,datadog.trace.context.*,datadog.trace.context"
}
- def "test Eclipse OSGi framework factory"() {
+ def "test OSGi framework factory"() {
setup:
def config = ["osgi.support.class.certificate": "false"]
- FrameworkFactory factory = new EquinoxFactory()
when:
Framework framework = factory.newFramework(config)
then:
framework != null
+
+ where:
+ factory | _
+ new EquinoxFactory() | _
+ new org.apache.felix.framework.FrameworkFactory() | _
}
}

Make OSGi instrumentation more generic

This should help it work with felix.

Also reduce log noise.