-
Notifications
You must be signed in to change notification settings - Fork 333
Enhance dd-java-agent ClassLoading #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| package datadog.trace.agent.test; | ||
|
|
||
| import io.opentracing.Scope; | ||
| import io.opentracing.Tracer; | ||
| import io.opentracing.util.GlobalTracer; | ||
| import java.io.File; | ||
| import java.io.FileOutputStream; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.lang.reflect.Field; | ||
| import java.net.URL; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.Callable; | ||
| import java.util.jar.JarEntry; | ||
| import java.util.jar.JarOutputStream; | ||
| import java.util.jar.Manifest; | ||
|
|
||
| /** | ||
| * Duplication of TestUtils in testing project. Cannot directly depend on testing project due to | ||
| * classpath issues. | ||
| */ | ||
| public class IntegrationTestUtils { | ||
|
|
||
| public static <T extends Object> Object runUnderTrace( | ||
| final String rootOperationName, final Callable<T> r) { | ||
| final Scope scope = GlobalTracer.get().buildSpan(rootOperationName).startActive(true); | ||
| try { | ||
| return r.call(); | ||
| } catch (final Exception e) { | ||
| throw new IllegalStateException(e); | ||
| } finally { | ||
| scope.close(); | ||
| } | ||
| } | ||
|
|
||
| /** Returns the classloader the core agent is running on. */ | ||
| public static ClassLoader getAgentClassLoader() { | ||
| Field classloaderField = null; | ||
| try { | ||
| Class<?> tracingAgentClass = | ||
| tracingAgentClass = | ||
| ClassLoader.getSystemClassLoader().loadClass("datadog.trace.agent.TracingAgent"); | ||
| classloaderField = tracingAgentClass.getDeclaredField("AGENT_CLASSLOADER"); | ||
| classloaderField.setAccessible(true); | ||
| return (ClassLoader) classloaderField.get(null); | ||
| } catch (Exception e) { | ||
| throw new IllegalStateException(e); | ||
| } finally { | ||
| if (null != classloaderField) { | ||
| classloaderField.setAccessible(false); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Create a temporary jar on the filesystem with the bytes of the given classes. | ||
| * | ||
| * <p>The jar file will be removed when the jvm exits. | ||
| * | ||
| * @param classes classes to package into the jar. | ||
| * @return the location of the newly created jar. | ||
| * @throws IOException | ||
| */ | ||
| public static URL createJarWithClasses(final Class<?>... classes) throws IOException { | ||
| final File tmpJar = File.createTempFile(UUID.randomUUID().toString() + "", ".jar"); | ||
| tmpJar.deleteOnExit(); | ||
|
|
||
| final Manifest manifest = new Manifest(); | ||
| final JarOutputStream target = new JarOutputStream(new FileOutputStream(tmpJar), manifest); | ||
| for (final Class<?> clazz : classes) { | ||
| addToJar(clazz, target); | ||
| } | ||
| target.close(); | ||
|
|
||
| return tmpJar.toURI().toURL(); | ||
| } | ||
|
|
||
| private static void addToJar(final Class<?> clazz, final JarOutputStream jarOutputStream) | ||
| throws IOException { | ||
| InputStream inputStream = null; | ||
| ClassLoader loader = clazz.getClassLoader(); | ||
| if (null == loader) { | ||
| // bootstrap resources can be fetched through the system loader | ||
| loader = ClassLoader.getSystemClassLoader(); | ||
| } | ||
| try { | ||
| final JarEntry entry = new JarEntry(getResourceName(clazz.getName())); | ||
| jarOutputStream.putNextEntry(entry); | ||
| inputStream = loader.getResourceAsStream(getResourceName(clazz.getName())); | ||
|
|
||
| final byte[] buffer = new byte[1024]; | ||
| while (true) { | ||
| final int count = inputStream.read(buffer); | ||
| if (count == -1) { | ||
| break; | ||
| } | ||
| jarOutputStream.write(buffer, 0, count); | ||
| } | ||
| jarOutputStream.closeEntry(); | ||
| } finally { | ||
| if (inputStream != null) { | ||
| inputStream.close(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public static void registerOrReplaceGlobalTracer(final Tracer tracer) { | ||
| try { | ||
| GlobalTracer.register(tracer); | ||
| } catch (final Exception e) { | ||
| // Force it anyway using reflection | ||
| Field field = null; | ||
| try { | ||
| field = GlobalTracer.class.getDeclaredField("tracer"); | ||
| field.setAccessible(true); | ||
| field.set(null, tracer); | ||
| } catch (final Exception e2) { | ||
| throw new IllegalStateException(e2); | ||
| } finally { | ||
| if (null != field) { | ||
| field.setAccessible(false); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!GlobalTracer.isRegistered()) { | ||
| throw new RuntimeException("Unable to register the global tracer."); | ||
| } | ||
| } | ||
|
|
||
| /** com.foo.Bar -> com/foo/Bar.class */ | ||
| public static String getResourceName(final String className) { | ||
| return className.replace('.', '/') + ".class"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // The shadowJar of this project will be injected into the JVM's bootstrap classloader | ||
| plugins { | ||
| id "com.github.johnrengelman.shadow" | ||
| } | ||
|
|
||
| // TODO include: opentracing, dd-ot-impl, dd-api | ||
| // Also: Move Patch Logger into this project | ||
| apply from: "${rootDir}/gradle/java.gradle" | ||
|
|
||
| dependencies { | ||
| compile project(':dd-trace-api') | ||
| compile deps.opentracing | ||
| compile deps.slf4j | ||
| compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25' | ||
| // ^ Generally a bad idea for libraries, but we're shadowing. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will need to apply the shadow rename rules here too.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind... I see it is handled by
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I wanted to keep the shadow rules in the same block so they're consistently applied. |
||
| } | ||
|
|
||
| jar { | ||
| classifier = 'unbundled' | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package datadog.trace.agent.bootstrap; | ||
|
|
||
| import java.sql.Connection; | ||
| import java.sql.PreparedStatement; | ||
| import java.util.Collections; | ||
| import java.util.Map; | ||
| import java.util.WeakHashMap; | ||
| import lombok.Data; | ||
|
|
||
| public class JDBCMaps { | ||
| public static final Map<Connection, DBInfo> connectionInfo = | ||
| Collections.synchronizedMap(new WeakHashMap<Connection, DBInfo>()); | ||
| public static final Map<PreparedStatement, String> preparedStatements = | ||
| Collections.synchronizedMap(new WeakHashMap<PreparedStatement, String>()); | ||
| public static final String UNKNOWN_QUERY = "Unknown Query"; | ||
|
|
||
| @Data | ||
| public static class DBInfo { | ||
| public static DBInfo UNKNOWN = new DBInfo("null", "unknown", null); | ||
| private final String url; | ||
| private final String type; | ||
| private final String user; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to do a whitelist, maybe using something like https://google.github.io/guava/releases/17.0/api/docs/index.html?com/google/common/reflect/ClassPath.html to scan the classpath.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat. I'll look into that.