Merge pull request #1555 from Xylus/feature/issue-1541

Adds callable statement tracing for JDBC libraries
Reviewed by @jaehong-kim
This commit is contained in:
Jaehong Kim
2016-02-26 10:54:29 +09:00
14 changed files with 726 additions and 88 deletions
@@ -1,86 +0,0 @@
package com.navercorp.pinpoint.plugin.jdbc.mariadb;
import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.*;
import static org.junit.Assert.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mariadb.jdbc.Driver;
import org.mariadb.jdbc.MariaDbConnection;
import org.mariadb.jdbc.MariaDbServerPreparedStatement;
import org.mariadb.jdbc.MariaDbStatement;
import ch.vorburger.mariadb4j.DB;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifierHolder;
import com.navercorp.pinpoint.test.plugin.Dependency;
import com.navercorp.pinpoint.test.plugin.JvmVersion;
import com.navercorp.pinpoint.test.plugin.PinpointPluginTestSuite;
import com.navercorp.pinpoint.test.plugin.Repository;
@RunWith(PinpointPluginTestSuite.class)
@JvmVersion(7)
@Repository("http://jcenter.bintray.com")
@Dependency({ "org.mariadb.jdbc:mariadb-java-client:[1.3.4,)", "ch.vorburger.mariaDB4j:mariaDB4j:2.1.3" })
public class MariaDBIT {
private static final String JDBC_URL = "jdbc:mariadb://127.0.0.1:13306/test";
private static final String PREPARED_STATEMENT = "SELECT * FROM playground where id = ?";
private DB db;
@Before
public void init() throws Exception {
db = DB.newEmbeddedDB(13306);
db.start();
db.createDB("test");
db.source("jdbc/mariadb/init.sql");
}
@Test
public void testConnection() throws Exception {
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
Connection connection = DriverManager.getConnection(JDBC_URL, "root", null);
// verifier.verifyTraceCount(1);
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("SELECT count(1) FROM playground");
result.first();
assertEquals(3, result.getInt(1));
verifier.printCache();
verifier.verifyTraceCount(2);
verifier.verifyTrace(event("MARIADB", Driver.class.getMethod("connect", String.class, Properties.class), cachedArgs(JDBC_URL)));
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", MariaDbStatement.class.getMethod("executeQuery", String.class), sql("SELECT count(0#) FROM playground", "1")));
PreparedStatement ps = connection.prepareStatement(PREPARED_STATEMENT);
ps.setInt(1, 3);
result = ps.executeQuery();
result.first();
assertEquals("THREE", result.getString(2));
verifier.printCache();
verifier.verifyTraceCount(2);
verifier.verifyTrace(event("MARIADB", MariaDbConnection.class.getMethod("prepareStatement", String.class), sql(PREPARED_STATEMENT, null)));
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", MariaDbServerPreparedStatement.class.getMethod("executeQuery"), sql(PREPARED_STATEMENT, null, "3")));
}
@After
public void cleanUp() throws Exception {
db.stop();
}
}
@@ -0,0 +1,104 @@
package com.navercorp.pinpoint.plugin.jdbc.mariadb;
import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.*;
import java.lang.reflect.Method;
import java.util.Properties;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifierHolder;
import com.navercorp.pinpoint.test.plugin.Dependency;
import com.navercorp.pinpoint.test.plugin.JvmVersion;
import com.navercorp.pinpoint.test.plugin.PinpointPluginTestSuite;
import com.navercorp.pinpoint.test.plugin.Repository;
@RunWith(PinpointPluginTestSuite.class)
@JvmVersion(7)
@Repository("http://jcenter.bintray.com")
@Dependency({"org.mariadb.jdbc:mariadb-java-client:[1.3.0,1.3.max]", "ch.vorburger.mariaDB4j:mariaDB4j:2.1.3"})
public class MariaDB_1_3_x_IT extends MariaDB_IT_Base {
// see CallableParameterMetaData#queryMetaInfos
private static final String CALLABLE_QUERY_META_INFOS_QUERY = "select param_list,returns from mysql.proc where db=database() and name='0$'";
@Test
public void testStatement() throws Exception {
super.executeStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
verifier.verifyTraceCount(2);
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbStatement#executeQuery(String)
Class<?> mariaDbStatementClass = Class.forName("org.mariadb.jdbc.MariaDbStatement");
Method executeQuery = mariaDbStatementClass.getDeclaredMethod("executeQuery", String.class);
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(STATEMENT_NORMALIZED_QUERY, "1")));
}
@Test
public void testPreparedStatement() throws Exception {
super.executePreparedStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
verifier.verifyTraceCount(3);
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbConnection#prepareStatement(String)
Class<?> mariaDbConnectionClass = Class.forName("org.mariadb.jdbc.MariaDbConnection");
Method prepareStatement = mariaDbConnectionClass.getDeclaredMethod("prepareStatement", String.class);
verifier.verifyTrace(event("MARIADB", prepareStatement, null, URL, DATABASE_NAME, sql(PREPARED_STATEMENT_QUERY, null)));
// MariaDbServerPreparedStatement#executeQuery
Class<?> mariaDbServerPreparedStatementClass = Class.forName("org.mariadb.jdbc.MariaDbServerPreparedStatement");
Method executeQuery = mariaDbServerPreparedStatementClass.getDeclaredMethod("executeQuery");
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(PREPARED_STATEMENT_QUERY, null, "3")));
}
@Test
public void testCallableStatement() throws Exception {
super.executeCallableStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
verifier.verifyTraceCount(5);
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbConnection#prepareCall(String)
Class<?> mariaDbConnectionClass = Class.forName("org.mariadb.jdbc.MariaDbConnection");
Method prepareCall = mariaDbConnectionClass.getDeclaredMethod("prepareCall", String.class);
verifier.verifyTrace(event("MARIADB", prepareCall, null, URL, DATABASE_NAME, sql(CALLABLE_STATEMENT_QUERY, null)));
// MariaDbClientCallableStatement#registerOutParameter(int, int)
Class<?> mariaDbClientCallableStatementClass = Class.forName("org.mariadb.jdbc.MariaDbCallableStatement");
Method registerOutParameter = mariaDbClientCallableStatementClass.getDeclaredMethod("registerOutParameter", int.class, int.class);
verifier.verifyTrace(event("MARIADB", registerOutParameter, null, URL, DATABASE_NAME, args(2, CALLABLE_STATMENT_OUTPUT_PARAM_TYPE)));
// MariaDbCallableStatement#executeQuery
Class<?> mariaDbCallableStatementClass = Class.forName("org.mariadb.jdbc.MariaDbCallableStatement");
Method callableStatementExecuteQuery = mariaDbCallableStatementClass.getDeclaredMethod("executeQuery");
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", callableStatementExecuteQuery, null, URL, DATABASE_NAME, sql(CALLABLE_STATEMENT_QUERY, null, CALLABLE_STATEMENT_INPUT_PARAM)));
// MariaDbStatement#executeQuery
Class<?> mariaDbStatementClass = Class.forName("org.mariadb.jdbc.MariaDbStatement");
Method statementExecuteQuery = mariaDbStatementClass.getDeclaredMethod("executeQuery", String.class);
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", statementExecuteQuery, null, URL, DATABASE_NAME, sql(CALLABLE_QUERY_META_INFOS_QUERY, PROCEDURE_NAME)));
}
}
@@ -0,0 +1,123 @@
/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.plugin.jdbc.mariadb;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifierHolder;
import com.navercorp.pinpoint.test.plugin.Dependency;
import com.navercorp.pinpoint.test.plugin.JvmVersion;
import com.navercorp.pinpoint.test.plugin.PinpointPluginTestSuite;
import com.navercorp.pinpoint.test.plugin.Repository;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.lang.reflect.Method;
import java.util.Properties;
import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.*;
/**
* @author HyunGil Jeong
*/
@RunWith(PinpointPluginTestSuite.class)
@JvmVersion(7)
@Repository("http://jcenter.bintray.com")
@Dependency({ "org.mariadb.jdbc:mariadb-java-client:[1.4.min,1.4.max]", "ch.vorburger.mariaDB4j:mariaDB4j:2.1.3" })
public class MariaDB_1_4_x_IT extends MariaDB_IT_Base {
// see CallableParameterMetaData#queryMetaInfos
private static final String CALLABLE_QUERY_META_INFOS_QUERY = "select param_list, returns, db, type from mysql.proc where db=DATABASE() and name=?";
@Test
public void testStatement() throws Exception {
super.executeStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbStatement#executeQuery(String)
Class<?> mariaDbStatementClass = Class.forName("org.mariadb.jdbc.MariaDbStatement");
Method executeQuery = mariaDbStatementClass.getDeclaredMethod("executeQuery", String.class);
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(STATEMENT_NORMALIZED_QUERY, "1")));
}
@Test
public void testPreparedStatement() throws Exception {
super.executePreparedStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
verifier.verifyTraceCount(3);
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbConnection#prepareStatement(String)
Class<?> mariaDbConnectionClass = Class.forName("org.mariadb.jdbc.MariaDbConnection");
Method prepareStatement = mariaDbConnectionClass.getDeclaredMethod("prepareStatement", String.class);
verifier.verifyTrace(event("MARIADB", prepareStatement, null, URL, DATABASE_NAME, sql(PREPARED_STATEMENT_QUERY, null)));
// MariaDbServerPreparedStatement#executeQuery
Class<?> mariaDbServerPreparedStatementClass = Class.forName("org.mariadb.jdbc.MariaDbServerPreparedStatement");
Method executeQuery = mariaDbServerPreparedStatementClass.getDeclaredMethod("executeQuery");
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(PREPARED_STATEMENT_QUERY, null, "3")));
}
@Test
public void testCallableStatement() throws Exception {
super.executeCallableStatement();
PluginTestVerifier verifier = PluginTestVerifierHolder.getInstance();
verifier.printCache();
verifier.verifyTraceCount(6);
// Driver#connect(String, Properties)
Class<?> driverClass = Class.forName("org.mariadb.jdbc.Driver");
Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
verifier.verifyTrace(event("MARIADB", connect, null, URL, DATABASE_NAME, cachedArgs(JDBC_URL)));
// MariaDbConnection#prepareCall(String)
Class<?> mariaDbConnectionClass = Class.forName("org.mariadb.jdbc.MariaDbConnection");
Method prepareCall = mariaDbConnectionClass.getDeclaredMethod("prepareCall", String.class);
verifier.verifyTrace(event("MARIADB", prepareCall, null, URL, DATABASE_NAME, sql(CALLABLE_STATEMENT_QUERY, null)));
// AbstractCallableProcedureStatement#registerOutParameter
Class<?> abstractCallableProcedureStatementClass = Class.forName("org.mariadb.jdbc.AbstractCallableProcedureStatement");
Method registerOutParameter = abstractCallableProcedureStatementClass.getMethod("registerOutParameter", int.class, int.class);
verifier.verifyTrace(event("MARIADB", registerOutParameter, null, URL, DATABASE_NAME, args(2, CALLABLE_STATMENT_OUTPUT_PARAM_TYPE)));
// MariaDbServerPreparedStatement#executeQuery
Class<?> mariaDbServerPreparedStatementClass = Class.forName("org.mariadb.jdbc.MariaDbServerPreparedStatement");
Method executeQuery = mariaDbServerPreparedStatementClass.getDeclaredMethod("executeQuery");
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(CALLABLE_STATEMENT_QUERY, null, CALLABLE_STATEMENT_INPUT_PARAM)));
// MariaDbConnection#prepareStatement(String)
Method prepareStatement = mariaDbConnectionClass.getDeclaredMethod("prepareStatement", String.class);
verifier.verifyTrace(event("MARIADB", prepareStatement, null, URL, DATABASE_NAME, sql(CALLABLE_QUERY_META_INFOS_QUERY, null)));
// MariaDbServerPreparedStatement#executeQuery
verifier.verifyTrace(event("MARIADB_EXECUTE_QUERY", executeQuery, null, URL, DATABASE_NAME, sql(CALLABLE_QUERY_META_INFOS_QUERY, null, PROCEDURE_NAME)));
}
}
@@ -0,0 +1,185 @@
/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.plugin.jdbc.mariadb;
import ch.vorburger.mariadb4j.DB;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author HyunGil Jeong
*/
public class MariaDB_IT_Base {
private static final int PORT = 13306;
protected static final String URL = "127.0.0.1:" + PORT;
protected static final String DATABASE_NAME = "test";
protected static final String JDBC_URL = "jdbc:mariadb://" + URL + "/" + DATABASE_NAME;
// for Statement
protected static final String STATEMENT_QUERY = "SELECT count(1) FROM playground";
protected static final String STATEMENT_NORMALIZED_QUERY = "SELECT count(0#) FROM playground";
// for Prepared Statement
protected static final String PREPARED_STATEMENT_QUERY = "SELECT * FROM playground where id = ?";
// for Callable Statement
protected static final String PROCEDURE_NAME = "getPlaygroundByName";
protected static final String CALLABLE_STATEMENT_QUERY = "{ CALL " + PROCEDURE_NAME + "(?, ?) }";
protected static final String CALLABLE_STATEMENT_INPUT_PARAM = "TWO";
protected static final int CALLABLE_STATMENT_OUTPUT_PARAM_TYPE = Types.INTEGER;
private static DB TEST_DATABASE;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
TEST_DATABASE = DB.newEmbeddedDB(PORT);
TEST_DATABASE.start();
TEST_DATABASE.createDB("test");
TEST_DATABASE.source("jdbc/mariadb/init.sql");
}
protected final void executeStatement() throws Exception {
Connection connection = null;
try {
connection = DriverManager.getConnection(JDBC_URL, "root", null);
Statement statement = null;
try {
statement = connection.createStatement();
ResultSet result = null;
try {
result = statement.executeQuery(STATEMENT_QUERY);
result.first();
assertEquals(3, result.getInt(1));
} finally {
closeResultSet(result);
}
} finally {
closeStatement(statement);
}
} finally {
closeConnection(connection);
}
}
protected final void executePreparedStatement() throws Exception {
Connection connection = null;
try {
connection = DriverManager.getConnection(JDBC_URL, "root", null);
PreparedStatement ps = null;
try {
ps = connection.prepareStatement(PREPARED_STATEMENT_QUERY);
ps.setInt(1, 3);
ResultSet result = null;
try {
result = ps.executeQuery();
result.first();
assertEquals("THREE", result.getString(2));
} finally {
closeResultSet(result);
}
} finally {
closeStatement(ps);
}
} finally {
closeConnection(connection);
}
}
protected final void executeCallableStatement() throws Exception {
final int expectedTotalCount = 3;
final int expectedMatchingCount = 1;
final int expectedMatchingId = 2;
final String outputParamCountName = "outputParamCount";
Connection conn = null;
try {
conn = DriverManager.getConnection(JDBC_URL, "root", null);
CallableStatement cs = null;
try {
cs = conn.prepareCall(CALLABLE_STATEMENT_QUERY);
cs.setString(1, CALLABLE_STATEMENT_INPUT_PARAM);
cs.registerOutParameter(2, CALLABLE_STATMENT_OUTPUT_PARAM_TYPE);
ResultSet rs = null;
try {
rs = cs.executeQuery();
int matchingCount = 0;
while (rs.next()) {
++matchingCount;
if (matchingCount > expectedMatchingCount) {
fail();
}
assertEquals(expectedMatchingId, rs.getInt(1));
assertEquals(CALLABLE_STATEMENT_INPUT_PARAM, rs.getString(2));
}
assertEquals(expectedMatchingCount, matchingCount);
} finally {
closeResultSet(rs);
}
final int totalCount = cs.getInt(outputParamCountName);
assertEquals(expectedTotalCount, totalCount);
} finally {
closeStatement(cs);
}
} finally {
closeConnection(conn);
}
}
private void closeConnection(Connection conn) throws SQLException {
if (conn != null) {
conn.close();
}
}
private void closeResultSet(ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
}
private void closeStatement(Statement statement) throws SQLException {
if (statement != null) {
statement.close();
}
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
TEST_DATABASE.stop();
}
}
+10 -1
View File
@@ -2,4 +2,13 @@ USE test;
CREATE TABLE IF NOT EXISTS playground (id int(5) NOT NULL, name varchar(50) DEFAULT NULL);
INSERT INTO playground VALUES (1, 'ONE');
INSERT INTO playground VALUES (2, 'TWO');
INSERT INTO playground VALUES (3, 'THREE');
INSERT INTO playground VALUES (3, 'THREE');
DELIMITER //
CREATE PROCEDURE getPlaygroundByName (IN inputParamName VARCHAR(50), OUT outputParamCount INT)
BEGIN
SELECT count(*) INTO outputParamCount FROM playground;
SELECT id, name FROM playground WHERE name = inputParamName ORDER BY id ASC;
END;
//
DELIMITER ;
@@ -0,0 +1,32 @@
/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetFilter;
/**
* @author HyunGil Jeong
*/
@TargetFilter(type = "com.navercorp.pinpoint.bootstrap.plugin.jdbc.PreparedStatementBindingMethodFilter", singleton = true)
public class CallableStatementBindVariableInterceptor extends PreparedStatementBindVariableInterceptor {
public CallableStatementBindVariableInterceptor(TraceContext traceContext) {
super(traceContext);
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor;
import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethods;
/**
* @author HyunGil Jeong
*/
@TargetMethods({
@TargetMethod(name = "execute"),
@TargetMethod(name = "executeQuery"),
@TargetMethod(name = "executeUpdate")
})
public class CallableStatementExecuteQueryInterceptor extends PreparedStatementExecuteQueryInterceptor {
public CallableStatementExecuteQueryInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
super(traceContext, descriptor);
}
public CallableStatementExecuteQueryInterceptor(TraceContext traceContext, MethodDescriptor descriptor, int maxSqlBindValue) {
super(traceContext, descriptor, maxSqlBindValue);
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2016 NAVER Corp.
*
* 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 com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor;
import com.navercorp.pinpoint.bootstrap.context.DatabaseInfo;
import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethods;
import com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor;
import com.navercorp.pinpoint.bootstrap.plugin.jdbc.UnKnownDatabaseInfo;
/**
* @author HyunGil Jeong
*/
@TargetMethods({
@TargetMethod(name = "registerOutParameter", paramTypes = {"int", "int"}),
@TargetMethod(name = "registerOutParameter", paramTypes = {"int", "int", "int"}),
@TargetMethod(name = "registerOutParameter", paramTypes = {"int", "int", "java.lang.String"})
})
public class CallableStatementRegisterOutParameterInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
public CallableStatementRegisterOutParameterInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
super(traceContext, descriptor);
}
@Override
protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) {
}
@Override
protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
DatabaseInfo databaseInfo = (target instanceof DatabaseInfoAccessor) ? ((DatabaseInfoAccessor)target)._$PINPOINT$_getDatabaseInfo() : null;
if (databaseInfo == null) {
databaseInfo = UnKnownDatabaseInfo.INSTANCE;
}
recorder.recordServiceType(databaseInfo.getType());
recorder.recordEndPoint(databaseInfo.getMultipleHost());
recorder.recordDestinationId(databaseInfo.getDatabaseId());
recorder.recordApi(methodDescriptor, args);
recorder.recordException(throwable);
}
}
@@ -38,7 +38,10 @@ import com.navercorp.pinpoint.bootstrap.util.InterceptorUtils;
@TargetMethod(name="prepareStatement", paramTypes={ "java.lang.String", "int[]" }),
@TargetMethod(name="prepareStatement", paramTypes={ "java.lang.String", "java.lang.String[]" }),
@TargetMethod(name="prepareStatement", paramTypes={ "java.lang.String", "int", "int" }),
@TargetMethod(name="prepareStatement", paramTypes={ "java.lang.String", "int", "int", "int" })
@TargetMethod(name="prepareStatement", paramTypes={ "java.lang.String", "int", "int", "int" }),
@TargetMethod(name="prepareCall", paramTypes={ "java.lang.String" }),
@TargetMethod(name="prepareCall", paramTypes={ "java.lang.String", "int", "int" }),
@TargetMethod(name="prepareCall", paramTypes={ "java.lang.String", "int", "int", "int" })
})
public class PreparedStatementCreateInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
@@ -43,6 +43,7 @@ public class CubridPlugin implements ProfilerPlugin, TransformTemplateAware {
addCUBRIDConnectionTransformer(config);
addCUBRIDDriverTransformer();
addCUBRIDPreparedStatementTransformer(config);
addCUBRIDCallableStatementTransformer();
addCUBRIDStatementTransformer();
}
@@ -113,6 +114,24 @@ public class CubridPlugin implements ProfilerPlugin, TransformTemplateAware {
}
});
}
private void addCUBRIDCallableStatementTransformer() {
transformTemplate.transform("cubrid.jdbc.driver.CUBRIDCallableStatement", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", CubridConstants.CUBRID_SCOPE);
return target.toBytecode();
}
});
}
private void addCUBRIDStatementTransformer() {
transformTemplate.transform("cubrid.jdbc.driver.CUBRIDStatement", new TransformCallback() {
@@ -42,6 +42,7 @@ public class JtdsPlugin implements ProfilerPlugin, TransformTemplateAware {
addConnectionTransformer(config);
addDriverTransformer();
addPreparedStatementTransformer(config);
addCallableStatementTransformer();
addStatementTransformer();
}
@@ -115,6 +116,24 @@ public class JtdsPlugin implements ProfilerPlugin, TransformTemplateAware {
});
}
private void addCallableStatementTransformer() {
transformTemplate.transform("net.sourceforge.jtds.jdbc.JtdsCallableStatement", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", JtdsConstants.JTDS_SCOPE);
return target.toBytecode();
}
});
}
private void addStatementTransformer() {
transformTemplate.transform("net.sourceforge.jtds.jdbc.JtdsStatement", new TransformCallback() {
@@ -44,7 +44,12 @@ public class MariaDBPlugin implements ProfilerPlugin, TransformTemplateAware {
addDriverTransformer();
addPreparedStatementTransformer(config);
addPreparedStatementBindVariableTransformer(config);
addCallableStatementTransformer();
addStatementTransformer();
// MariaDb 1.3.x's CallableStatements are completely separated from PreparedStatements (similar to MySQL)
// Separate interceptors must be injected.
add_1_3_x_CallableStatementTransformer(config);
}
private void addConnectionTransformer(final MariaDBConfig config) {
@@ -206,6 +211,54 @@ public class MariaDBPlugin implements ProfilerPlugin, TransformTemplateAware {
}
private void addCallableStatementTransformer() {
TransformCallback transformer = new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", MariaDBConstants.MARIADB_SCOPE);
return target.toBytecode();
}
};
transformTemplate.transform("org.mariadb.jdbc.AbstractCallableProcedureStatement", transformer);
transformTemplate.transform("org.mariadb.jdbc.AbstractCallableFunctionStatement", transformer);
}
private void add_1_3_x_CallableStatementTransformer(final MariaDBConfig config) {
transformTemplate.transform("org.mariadb.jdbc.MariaDbCallableStatement", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
int maxBindValueSize = config.getMaxSqlBindValueSize();
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementExecuteQueryInterceptor", va(maxBindValueSize), MariaDBConstants.MARIADB_SCOPE);
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", MariaDBConstants.MARIADB_SCOPE);
if (config.isTraceSqlBindValue()) {
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementBindVariableInterceptor", MariaDBConstants.MARIADB_SCOPE);
}
return target.toBytecode();
}
});
}
@Override
public void setTransformTemplate(TransformTemplate transformTemplate) {
this.transformTemplate = transformTemplate;
@@ -44,10 +44,12 @@ public class MySqlPlugin implements ProfilerPlugin, TransformTemplateAware {
addDriverTransformer();
addStatementTransformer();
addPreparedStatementTransformer(config);
addCallableStatementTransformer(config);
// From MySQL driver 5.1.x, backward compatibility is broken.
// Driver returns not com.mysql.jdbc.Connection but com.mysql.jdbc.JDBC4Connection which extends com.mysql.jdbc.ConnectionImpl from 5.1.x
addJDBC4PreparedStatementTransformer(config);
addJDBC4CallableStatementTransformer(config);
}
private void addConnectionTransformer(final MySqlConfig config) {
@@ -127,6 +129,32 @@ public class MySqlPlugin implements ProfilerPlugin, TransformTemplateAware {
});
}
private void addCallableStatementTransformer(final MySqlConfig config) {
transformTemplate.transform("com.mysql.jdbc.CallableStatement", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
int maxBindValueSize = config.getMaxSqlBindValueSize();
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementExecuteQueryInterceptor", va(maxBindValueSize), MySqlConstants.MYSQL_SCOPE);
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", MySqlConstants.MYSQL_SCOPE);
if (config.isTraceSqlBindValue()) {
final PreparedStatementBindingMethodFilter excludes = PreparedStatementBindingMethodFilter.excludes("setRowId", "setNClob", "setSQLXML");
target.addScopedInterceptor(excludes, "com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementBindVariableInterceptor", MySqlConstants.MYSQL_SCOPE, ExecutionPolicy.BOUNDARY);
}
return target.toBytecode();
}
});
}
private void addJDBC4PreparedStatementTransformer(final MySqlConfig config) {
transformTemplate.transform("com.mysql.jdbc.JDBC4PreparedStatement", new TransformCallback() {
@@ -144,6 +172,22 @@ public class MySqlPlugin implements ProfilerPlugin, TransformTemplateAware {
});
}
private void addJDBC4CallableStatementTransformer(final MySqlConfig config) {
transformTemplate.transform("com.mysql.jdbc.JDBC4CallableStatement", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
if (config.isTraceSqlBindValue()) {
final PreparedStatementBindingMethodFilter includes = PreparedStatementBindingMethodFilter.includes("setRowId", "setNClob", "setSQLXML");
target.addScopedInterceptor(includes, "com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementBindVariableInterceptor", MySqlConstants.MYSQL_SCOPE, ExecutionPolicy.BOUNDARY);
}
return target.toBytecode();
}
});
}
private void addStatementTransformer() {
TransformCallback transformer = new TransformCallback() {
@@ -37,6 +37,8 @@ public class OraclePlugin implements ProfilerPlugin, TransformTemplateAware {
private static final String CLASS_STATEMENT = "oracle.jdbc.driver.OracleStatement";
private static final String CLASS_PREPARED_STATEMENT_WRAPPER = "oracle.jdbc.driver.OraclePreparedStatementWrapper";
private static final String CLASS_PREPARED_STATEMENT = "oracle.jdbc.driver.OraclePreparedStatement";
private static final String CLASS_CALLABLE_STATEMENT_WRAPPER = "oracle.jdbc.driver.OracleCallableStatementWrapper";
private static final String CLASS_CALLABLE_STATEMENT = "oracle.jdbc.driver.OracleCallableStatement";
private TransformTemplate transformTemplate;
@@ -47,6 +49,7 @@ public class OraclePlugin implements ProfilerPlugin, TransformTemplateAware {
addConnectionTransformer(config);
addDriverTransformer();
addPreparedStatementTransformer(config);
addCallableStatementTransformer();
addStatementTransformer();
}
@@ -128,6 +131,32 @@ public class OraclePlugin implements ProfilerPlugin, TransformTemplateAware {
transformTemplate.transform(CLASS_PREPARED_STATEMENT_WRAPPER, transformer);
}
private void addCallableStatementTransformer() {
TransformCallback transformer = new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
if (className.equals(CLASS_CALLABLE_STATEMENT)) {
if (instrumentor.exist(loader, CLASS_CALLABLE_STATEMENT_WRAPPER)) {
return null;
}
}
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor");
target.addField("com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor");
target.addScopedInterceptor("com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor", OracleConstants.ORACLE_SCOPE);
return target.toBytecode();
}
};
transformTemplate.transform(CLASS_CALLABLE_STATEMENT, transformer);
transformTemplate.transform(CLASS_CALLABLE_STATEMENT_WRAPPER, transformer);
}
private void addStatementTransformer() {
TransformCallback transformer = new TransformCallback() {