Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions dd-java-agent-ittests/dd-java-agent-ittests.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ apply from: "${rootDir}/gradle/java.gradle"

description = 'dd-java-agent-ittests'

evaluationDependsOn(':dd-java-agent:tooling')
compileTestJava.dependsOn tasks.getByPath(':dd-java-agent:tooling:testClasses')
evaluationDependsOn(':dd-java-agent:agent-tooling')
compileTestJava.dependsOn tasks.getByPath(':dd-java-agent:agent-tooling:testClasses')

if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
sourceSets {
test {
groovy {
// These classes use Ratpack which requires Java 8. (Currently also incompatible with Java 9.)
exclude '**/TestHttpServer.groovy', '**/ApacheHttpClientTest.groovy'

// log rewrite incompatible with Java9 test classpath
exclude '**/TestLoggerRewrite.groovy'
}
}
}
Expand All @@ -26,12 +23,6 @@ dependencies {
testCompile deps.opentracingMock
testCompile deps.testLogging

testCompile(project(':dd-java-agent:testing')) {
// Otherwise this can bring in classes that aren't "shadowed"
// that conflicts with those that are which are in the agent.
transitive = false
}

testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2'
testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2'
// run embeded mongodb for integration testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package datadog.trace.agent.integration.httpclient
import datadog.opentracing.DDSpan
import datadog.opentracing.DDTracer
import datadog.trace.agent.integration.TestHttpServer
import datadog.trace.agent.test.TestUtils
import datadog.trace.agent.test.IntegrationTestUtils
import datadog.trace.api.DDSpanTypes
import datadog.trace.common.writer.ListWriter
import io.opentracing.tag.Tags
Expand All @@ -23,7 +23,7 @@ class ApacheHttpClientTest extends Specification {
def tracer = new DDTracer(writer)

def setupSpec() {
TestUtils.registerOrReplaceGlobalTracer(tracer)
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer)
TestHttpServer.startServer()
}

Expand All @@ -40,7 +40,7 @@ class ApacheHttpClientTest extends Specification {
final HttpClientBuilder builder = HttpClientBuilder.create()

final HttpClient client = builder.build()
TestUtils.runUnderTrace("someTrace") {
IntegrationTestUtils.runUnderTrace("someTrace") {
try {
HttpResponse response =
client.execute(new HttpGet(new URI("http://localhost:" + TestHttpServer.getPort())))
Expand Down Expand Up @@ -88,7 +88,7 @@ class ApacheHttpClientTest extends Specification {
final HttpClientBuilder builder = HttpClientBuilder.create()

final HttpClient client = builder.build()
TestUtils.runUnderTrace("someTrace") {
IntegrationTestUtils.runUnderTrace("someTrace") {
try {
HttpGet request = new HttpGet(new URI("http://localhost:"
+ TestHttpServer.getPort()))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package datadog.trace.agent.integration.jdbc

import datadog.opentracing.DDTracer
import datadog.trace.agent.test.TestUtils
import datadog.trace.agent.test.IntegrationTestUtils
import datadog.trace.common.writer.ListWriter
import org.apache.derby.jdbc.EmbeddedDriver
import org.h2.Driver
Expand Down Expand Up @@ -42,7 +42,7 @@ class JDBCInstrumentationTest extends Specification {
}

def setup() {
TestUtils.registerOrReplaceGlobalTracer(tracer)
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer)
writer.start()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package datadog.trace.agent;

import static datadog.trace.agent.test.TestUtils.createJarWithClasses;
import static datadog.trace.agent.test.IntegrationTestUtils.createJarWithClasses;

import datadog.trace.api.Trace;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Assert;
import org.junit.Test;

// TODO: move to spock
public class ClassLoaderTest {

/** Assert that we can instrument classloaders which cannot resolve agent advice classes. */
Expand All @@ -34,4 +35,6 @@ public static class ClassToInstrument {
@Trace
public static void someMethod() {}
}

// TODO: Write test: assert our agent resource locator can locate resources from the bootstrap jar
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package datadog.trace.agent;

import com.google.common.collect.MapMaker;
import datadog.trace.agent.test.IntegrationTestUtils;
import org.junit.Assert;
import org.junit.Test;

// TODO: move to spock
// TODO: merge with log rewrite test
public class ShadowPackageRenamingTest {

@Test
public void agentDependenciesRenamed() throws Exception {
final Class<?> ddClass =
ClassLoader.getSystemClassLoader().loadClass("datadog.trace.agent.TracingAgent");
IntegrationTestUtils.getAgentClassLoader()
.loadClass("datadog.trace.agent.tooling.AgentInstaller");

final String userGuava =
MapMaker.class.getProtectionDomain().getCodeSource().getLocation().getFile();
Expand All @@ -24,10 +29,16 @@ public void agentDependenciesRenamed() throws Exception {
ddClass.getProtectionDomain().getCodeSource().getLocation().getFile();

Assert.assertTrue(
"TracingAgent should reside in the -javaagent jar: " + agentSource,
agentSource.matches(".*/dd-java-agent[^/]*.jar"));
"AgentInstaller should reside in the tmp tooling jar: " + agentSource,
agentSource.matches(".*/agent-tooling-and-instrumentation[^/]*.jar"));
Assert.assertEquals("DD guava dep must be loaded from agent jar.", agentSource, agentGuavaDep);
Assert.assertNotEquals(
"User guava dep must not be loaded from agent jar.", agentSource, userGuava);
}

// TODO: Write test
// for every class in bootstrap jar:
// assert class not present in agent jar
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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.


// TODO: Write test: assert agent classes not visible
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import datadog.opentracing.DDSpan;
import datadog.opentracing.DDTracer;
import datadog.opentracing.decorators.ErrorFlag;
import datadog.trace.agent.test.IntegrationTestUtils;
import datadog.trace.agent.test.SayTracedHello;
import datadog.trace.agent.test.TestUtils;
import datadog.trace.common.writer.ListWriter;
import io.opentracing.util.GlobalTracer;
import java.io.PrintWriter;
Expand All @@ -20,7 +20,7 @@ public class TraceAnnotationsTest {

@Before
public void beforeTest() throws Exception {
TestUtils.registerOrReplaceGlobalTracer(tracer);
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer);

writer.start();
assertThat(GlobalTracer.isRegistered()).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.mongodb.async.client.MongoDatabase;
import datadog.opentracing.DDSpan;
import datadog.opentracing.DDTracer;
import datadog.trace.agent.test.TestUtils;
import datadog.trace.agent.test.IntegrationTestUtils;
import datadog.trace.common.writer.ListWriter;
import io.opentracing.tag.Tags;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -27,7 +27,7 @@ public class MongoAsyncClientInstrumentationTest {

@BeforeClass
public static void setup() throws Exception {
TestUtils.registerOrReplaceGlobalTracer(tracer);
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer);
MongoClientInstrumentationTest.startLocalMongo();
client = MongoClients.create("mongodb://" + MONGO_HOST + ":" + MONGO_PORT);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.mongodb.client.MongoDatabase;
import datadog.opentracing.DDSpan;
import datadog.opentracing.DDTracer;
import datadog.trace.agent.test.TestUtils;
import datadog.trace.agent.test.IntegrationTestUtils;
import datadog.trace.common.writer.ListWriter;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
Expand Down Expand Up @@ -60,7 +60,7 @@ public static void stopLocalMongo() throws Exception {

@BeforeClass
public static void setup() throws Exception {
TestUtils.registerOrReplaceGlobalTracer(tracer);
IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer);
startLocalMongo();

client = new MongoClient(MONGO_HOST, MONGO_PORT);
Expand Down
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";
}
}
20 changes: 20 additions & 0 deletions dd-java-agent/agent-bootstrap/agent-bootstrap.gradle
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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to apply the shadow rename rules here too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind... I see it is handled by dd-java-agent instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Up @@ -11,6 +11,7 @@
* <p>Shadow rewrites will redirect those calls to this class, which will return a safe logger.
*/
public class PatchLogger {
// FIXME: Use "datadog.trace.agent.bootstrap" package name for clarity
private static final PatchLogger SAFE_LOGGER = new PatchLogger("datadogSafeLogger", "bundle");

public static PatchLogger getLogger(String name) {
Expand Down
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;
}
}
Loading