/*
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glowroot.agent.plugin.jdbc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.glowroot.agent.it.harness.AppUnderTest;
import org.glowroot.agent.it.harness.Container;
import org.glowroot.agent.it.harness.Containers;
import org.glowroot.agent.it.harness.TraceEntryMarker;
import org.glowroot.agent.it.harness.TransactionMarker;
import org.glowroot.wire.api.model.TraceOuterClass.Trace;
import static org.assertj.core.api.Assertions.assertThat;
public class JdbcPluginIT {
private static final String PLUGIN_ID = "jdbc";
private static Container container;
@BeforeClass
public static void setUp() throws Exception {
container = Containers.create();
}
@AfterClass
public static void tearDown() throws Exception {
container.close();
}
@After
public void afterEachTest() throws Exception {
container.checkAndReset();
}
@Test
public void testCallableStatement() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureBindParameters", true);
// when
Trace trace = container.execute(ExecuteCallableStatement.class);
// then
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("insert into employee (name, misc) values (?, ?)");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" ['jane', NULL]");
assertThat(i.hasNext()).isFalse();
}
@Test
public void testWithoutResultSetValueTimerNormal() throws Exception {
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
boolean found =
findExtendedTimerName(trace.getHeader().getMainThreadRootTimer(), "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testWithResultSetValueTimerNormal() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureResultSetGet", true);
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testWithoutResultSetValueTimerUnderSeparateTraceEntry() throws Exception {
// when
Trace trace = container.execute(GetResultSetValueUnderSeparateTraceEntry.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testWithResultSetValueTimerUnderSeparateTraceEntry() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureResultSetGet", true);
// when
Trace trace = container.execute(GetResultSetValueUnderSeparateTraceEntry.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isTrue();
}
@Test
public void testResultSetValueTimerUsingColumnName() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureResultSetGet", true);
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResultsUsingColumnName.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testResultSetValueTimerUsingColumnNameUnderSeparateTraceEntry() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureResultSetGet", true);
// when
Trace trace = container.execute(
ExecuteStatementAndIterateOverResultsUsingColumnNameUnderSeparateTraceEntry.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isTrue();
}
@Test
public void testWithResultSetNavigateTimerNormal() throws Exception {
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testWithResultSetNavigateTimerUnderSeparateTraceEntry() throws Exception {
// when
Trace trace = container.execute(IterateOverResultsUnderSeparateTraceEntry.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isTrue();
}
@Test
public void testWithoutResultSetNavigateTimerUnderSeparateTraceEntry() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "captureResultSetNavigate",
false);
// when
Trace trace = container.execute(IterateOverResultsUnderSeparateTraceEntry.class);
// then
boolean found = findExtendedTimerName(trace, "jdbc execute");
assertThat(found).isFalse();
}
@Test
public void testDefaultStackTraceThreshold() throws Exception {
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("select * from employee");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows");
assertThat(entry.getLocationStackTraceElementList()).isEmpty();
assertThat(i.hasNext()).isFalse();
}
@Test
public void testZeroStackTraceThreshold() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "stackTraceThresholdMillis", 0.0);
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("select * from employee");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows");
assertThat(entry.getLocationStackTraceElementList()).isNotEmpty();
assertThat(i.hasNext()).isFalse();
}
@Test
public void testNullStackTraceThreshold() throws Exception {
// given
container.getConfigService().setPluginProperty(PLUGIN_ID, "stackTraceThresholdMillis",
(Double) null);
// when
Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class);
// then
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("select * from employee");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows");
assertThat(entry.getLocationStackTraceElementList()).isEmpty();
assertThat(i.hasNext()).isFalse();
}
private static boolean findExtendedTimerName(Trace trace, String timerName) {
return findExtendedTimerName(trace.getHeader().getMainThreadRootTimer(), timerName);
}
private static boolean findExtendedTimerName(Trace.Timer timer, String timerName) {
if (timer.getName().equals(timerName) && timer.getExtended()) {
return true;
}
for (Trace.Timer nestedTimer : timer.getChildTimerList()) {
if (findExtendedTimerName(nestedTimer, timerName)) {
return true;
}
}
return false;
}
public static class ExecuteStatementAndIterateOverResults
implements AppUnderTest, TransactionMarker {
private Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
Statement statement = connection.createStatement();
try {
statement.execute("select * from employee");
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getString(1);
}
} finally {
statement.close();
}
}
}
public static class ExecuteLotsOfStatementAndIterateOverResults
implements AppUnderTest, TransactionMarker {
private Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
Statement statement = connection.createStatement();
try {
for (int i = 0; i < 4000; i++) {
statement.execute("select * from employee");
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getString(1);
}
}
} finally {
statement.close();
}
}
}
public static class IterateOverResultsUnderSeparateTraceEntry
implements AppUnderTest, TransactionMarker, TraceEntryMarker {
private Connection connection;
private Statement statement;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
statement = connection.createStatement();
try {
statement.execute("select * from employee");
traceEntryMarker();
} finally {
statement.close();
}
}
@Override
public void traceEntryMarker() throws SQLException {
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getString(1);
}
}
}
public static class GetResultSetValueUnderSeparateTraceEntry
implements AppUnderTest, TransactionMarker, TraceEntryMarker {
private Connection connection;
private ResultSet rs;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
Statement statement = connection.createStatement();
try {
statement.execute("select * from employee");
rs = statement.getResultSet();
while (rs.next()) {
traceEntryMarker();
}
} finally {
statement.close();
}
}
@Override
public void traceEntryMarker() throws SQLException {
rs.getString(1);
}
}
public static class ExecuteStatementAndIterateOverResultsUsingColumnName
implements AppUnderTest, TransactionMarker {
private Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
Statement statement = connection.createStatement();
try {
statement.execute("select * from employee");
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getString("name");
}
} finally {
statement.close();
}
}
}
public static class ExecuteStatementAndIterateOverResultsUsingColumnNameUnderSeparateTraceEntry
implements AppUnderTest, TransactionMarker, TraceEntryMarker {
private Connection connection;
private Statement statement;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
statement = connection.createStatement();
try {
statement.execute("select * from employee");
traceEntryMarker();
} finally {
statement.close();
}
}
@Override
public void traceEntryMarker() throws SQLException {
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getString("name");
}
}
}
public static class ExecuteCallableStatement implements AppUnderTest, TransactionMarker {
private Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
CallableStatement callableStatement =
connection.prepareCall("insert into employee (name, misc) values (?, ?)");
try {
callableStatement.setString(1, "jane");
callableStatement.setNull(2, Types.BINARY);
callableStatement.execute();
} finally {
callableStatement.close();
}
}
}
public static class AccessMetaData implements AppUnderTest, TransactionMarker {
private Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
connection.setAutoCommit(false);
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
@Override
public void transactionMarker() throws Exception {
connection.getMetaData().getTables(null, null, null, null);
}
}
}