Skip to content

Commit ba15c66

Browse files
committed
add test
1 parent ccb9505 commit ba15c66

File tree

2 files changed

+131
-24
lines changed

2 files changed

+131
-24
lines changed

instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/JdbcInstrumentationTest.java

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,7 @@ void testProxyStatement() throws Exception {
13301330
cleanup.deferCleanup(statement);
13311331
cleanup.deferCleanup(connection);
13321332

1333-
Statement proxyStatement = ProxyStatementFactory.proxyStatement(statement);
1333+
Statement proxyStatement = ProxyStatementFactory.proxyStatementWithCustomClassLoader(statement);
13341334
ResultSet resultSet =
13351335
testing.runWithSpan("parent", () -> proxyStatement.executeQuery("SELECT 3"));
13361336

@@ -1737,4 +1737,100 @@ static Stream<Arguments> transactionOperationsStream() throws SQLException {
17371737
Arguments.of(
17381738
"hsqldb", new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null), "SA", "hsqldb:mem:"));
17391739
}
1740+
1741+
private static PreparedStatement wrapPreparedStatement(PreparedStatement statement) {
1742+
return ProxyStatementFactory.proxyPreparedStatement(
1743+
(proxy, method, args) -> {
1744+
if ("isWrapperFor".equals(method.getName())
1745+
&& args.length == 1
1746+
&& args[0] == PreparedStatement.class) {
1747+
return true;
1748+
}
1749+
if ("unwrap".equals(method.getName())
1750+
&& args.length == 1
1751+
&& args[0] == PreparedStatement.class) {
1752+
return statement;
1753+
}
1754+
return testing.runWithSpan("wrapper", () -> method.invoke(statement, args));
1755+
});
1756+
}
1757+
1758+
// test that tracing does not start from a wrapper
1759+
// regression test for
1760+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/14733
1761+
@Test
1762+
void testPreparedStatementWrapper() throws SQLException {
1763+
Connection connection = new org.h2.Driver().connect(jdbcUrls.get("h2"), null);
1764+
Connection proxyConnection =
1765+
ProxyStatementFactory.proxy(
1766+
Connection.class,
1767+
(proxy, method, args) -> {
1768+
// we don't implement unwrapping here as that would also cause the executeQuery
1769+
// instrumentation to get skipped for the prepared statement wrapper
1770+
if ("prepareStatement".equals(method.getName())) {
1771+
return wrapPreparedStatement((PreparedStatement) method.invoke(connection, args));
1772+
}
1773+
return method.invoke(connection, args);
1774+
});
1775+
PreparedStatement statement = proxyConnection.prepareStatement("SELECT 3");
1776+
cleanup.deferCleanup(statement);
1777+
cleanup.deferCleanup(connection);
1778+
1779+
ResultSet resultSet = testing.runWithSpan("parent", () -> statement.executeQuery());
1780+
1781+
resultSet.next();
1782+
assertThat(resultSet.getInt(1)).isEqualTo(3);
1783+
testing.waitAndAssertTraces(
1784+
trace ->
1785+
trace.hasSpansSatisfyingExactly(
1786+
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
1787+
span ->
1788+
span.hasName("wrapper").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0)),
1789+
span ->
1790+
span.hasName("SELECT " + dbNameLower)
1791+
.hasKind(SpanKind.CLIENT)
1792+
.hasParent(trace.getSpan(1))));
1793+
}
1794+
1795+
// test that tracing does not start from a wrapper
1796+
// regression test for
1797+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/14733
1798+
@Test
1799+
void testStatementWrapper() throws SQLException {
1800+
Connection connection = new org.h2.Driver().connect(jdbcUrls.get("h2"), null);
1801+
Statement statement = connection.createStatement();
1802+
cleanup.deferCleanup(statement);
1803+
cleanup.deferCleanup(connection);
1804+
1805+
Statement proxyStatement =
1806+
ProxyStatementFactory.proxyStatement(
1807+
(proxy, method, args) -> {
1808+
if ("isWrapperFor".equals(method.getName())
1809+
&& args.length == 1
1810+
&& args[0] == Statement.class) {
1811+
return true;
1812+
}
1813+
if ("unwrap".equals(method.getName())
1814+
&& args.length == 1
1815+
&& args[0] == Statement.class) {
1816+
return statement;
1817+
}
1818+
return testing.runWithSpan("wrapper", () -> method.invoke(statement, args));
1819+
});
1820+
ResultSet resultSet =
1821+
testing.runWithSpan("parent", () -> proxyStatement.executeQuery("SELECT 3"));
1822+
1823+
resultSet.next();
1824+
assertThat(resultSet.getInt(1)).isEqualTo(3);
1825+
testing.waitAndAssertTraces(
1826+
trace ->
1827+
trace.hasSpansSatisfyingExactly(
1828+
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
1829+
span ->
1830+
span.hasName("wrapper").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0)),
1831+
span ->
1832+
span.hasName("SELECT " + dbNameLower)
1833+
.hasKind(SpanKind.CLIENT)
1834+
.hasParent(trace.getSpan(1))));
1835+
}
17401836
}

instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/ProxyStatementFactory.java

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,50 +12,61 @@
1212

1313
public final class ProxyStatementFactory {
1414

15-
public static Statement proxyStatement(Statement statement) throws Exception {
15+
public static Statement proxyStatementWithCustomClassLoader(Statement statement)
16+
throws Exception {
1617
TestClassLoader classLoader = new TestClassLoader(ProxyStatementFactory.class.getClassLoader());
1718
Class<?> testInterface = classLoader.loadClass(TestInterface.class.getName());
1819
if (testInterface.getClassLoader() != classLoader) {
1920
throw new IllegalStateException("wrong class loader");
2021
}
2122
InvocationHandler invocationHandler = (proxy, method, args) -> method.invoke(statement, args);
22-
Statement proxyStatement =
23-
(Statement)
24-
Proxy.newProxyInstance(
25-
classLoader, new Class<?>[] {Statement.class, testInterface}, invocationHandler);
26-
// adding package private interface TestInterface to jdk proxy forces defining the proxy class
27-
// in the same package as the package private interface
28-
if (!proxyStatement
29-
.getClass()
30-
.getName()
31-
.startsWith("io.opentelemetry.javaagent.instrumentation.jdbc.test")) {
32-
throw new IllegalStateException("proxy statement is in wrong package");
33-
}
23+
return proxy(
24+
Statement.class,
25+
classLoader,
26+
new Class<?>[] {Statement.class, testInterface},
27+
invocationHandler);
28+
}
3429

35-
return proxyStatement;
30+
public static Statement proxyStatement(InvocationHandler invocationHandler) {
31+
return proxy(Statement.class, invocationHandler);
3632
}
3733

3834
public static PreparedStatement proxyPreparedStatement(PreparedStatement statement) {
3935
InvocationHandler invocationHandler = (proxy, method, args) -> method.invoke(statement, args);
40-
PreparedStatement proxyStatement =
41-
(PreparedStatement)
42-
Proxy.newProxyInstance(
43-
ProxyStatementFactory.class.getClassLoader(),
44-
new Class<?>[] {PreparedStatement.class, TestInterface.class},
45-
invocationHandler);
36+
return proxyPreparedStatement(invocationHandler);
37+
}
38+
39+
public static PreparedStatement proxyPreparedStatement(InvocationHandler invocationHandler) {
40+
return proxy(PreparedStatement.class, invocationHandler);
41+
}
42+
43+
public static <T> T proxy(Class<T> clazz, InvocationHandler invocationHandler) {
44+
return proxy(
45+
clazz,
46+
ProxyStatementFactory.class.getClassLoader(),
47+
new Class<?>[] {clazz, TestInterface.class},
48+
invocationHandler);
49+
}
50+
51+
public static <T> T proxy(
52+
Class<T> clazz,
53+
ClassLoader classLoader,
54+
Class<?>[] interfaces,
55+
InvocationHandler invocationHandler) {
56+
T proxy = clazz.cast(Proxy.newProxyInstance(classLoader, interfaces, invocationHandler));
4657

4758
// adding package private interface TestInterface to jdk proxy forces defining the proxy class
4859
// in the same package as the package private interface
4960
// by default we ignore jdk proxies, having the proxy in a different package ensures it gets
5061
// instrumented
51-
if (!proxyStatement
62+
if (!proxy
5263
.getClass()
5364
.getName()
5465
.startsWith("io.opentelemetry.javaagent.instrumentation.jdbc.test")) {
55-
throw new IllegalStateException("proxy statement is in wrong package");
66+
throw new IllegalStateException("proxy is in wrong package");
5667
}
5768

58-
return proxyStatement;
69+
return proxy;
5970
}
6071

6172
private ProxyStatementFactory() {}

0 commit comments

Comments
 (0)