aboutsummaryrefslogtreecommitdiffstats
path: root/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
diff options
context:
space:
mode:
Diffstat (limited to 'dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java')
-rw-r--r--dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java342
1 files changed, 342 insertions, 0 deletions
diff --git a/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
new file mode 100644
index 0000000..e3b160b
--- /dev/null
+++ b/dblib/common/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
@@ -0,0 +1,342 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * openecomp
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.jdbc.pool.interceptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Implementation of <b>JdbcInterceptor</b> that proxies resultSets and statements.
+ * @author Guillermo Fernandes
+ */
+public class StatementDecoratorInterceptor extends AbstractCreateStatementInterceptor {
+
+ private static final Log logger = LogFactory.getLog(StatementDecoratorInterceptor.class);
+
+ protected static final String EXECUTE_QUERY = "executeQuery";
+ protected static final String GET_GENERATED_KEYS = "getGeneratedKeys";
+ protected static final String GET_RESULTSET = "getResultSet";
+
+ protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET};
+
+ /**
+ * the constructors that are used to create statement proxies
+ */
+ protected static final Constructor<?>[] constructors = new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT];
+
+ /**
+ * the constructor to create the resultSet proxies
+ */
+ protected static Constructor<?> resultSetConstructor = null;
+
+ @Override
+ public void closeInvoked() {
+ // nothing to do
+ }
+
+ /**
+ * Creates a constructor for a proxy class, if one doesn't already exist
+ *
+ * @param idx
+ * - the index of the constructor
+ * @param clazz
+ * - the interface that the proxy will implement
+ * @return - returns a constructor used to create new instances
+ * @throws NoSuchMethodException Constructor not found
+ */
+ protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
+ if (constructors[idx] == null) {
+ Class<?> proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(),
+ new Class[] { clazz });
+ constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
+ }
+ return constructors[idx];
+ }
+
+ protected Constructor<?> getResultSetConstructor() throws NoSuchMethodException {
+ if (resultSetConstructor == null) {
+ Class<?> proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(),
+ new Class[] { ResultSet.class });
+ resultSetConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
+ }
+ return resultSetConstructor;
+ }
+
+ /**
+ * Creates a statement interceptor to monitor query response times
+ */
+ @Override
+ public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
+ try {
+ String name = method.getName();
+ Constructor<?> constructor = null;
+ String sql = null;
+ if (compare(CREATE_STATEMENT, name)) {
+ // createStatement
+ constructor = getConstructor(CREATE_STATEMENT_IDX, Statement.class);
+ } else if (compare(PREPARE_STATEMENT, name)) {
+ // prepareStatement
+ constructor = getConstructor(PREPARE_STATEMENT_IDX, PreparedStatement.class);
+ sql = (String)args[0];
+ } else if (compare(PREPARE_CALL, name)) {
+ // prepareCall
+ constructor = getConstructor(PREPARE_CALL_IDX, CallableStatement.class);
+ sql = (String)args[0];
+ } else {
+ // do nothing, might be a future unsupported method
+ // so we better bail out and let the system continue
+ return statement;
+ }
+ return createDecorator(proxy, method, args, statement, constructor, sql);
+ } catch (Exception x) {
+ if (x instanceof InvocationTargetException) {
+ Throwable cause = x.getCause();
+ if (cause instanceof ThreadDeath) {
+ throw (ThreadDeath) cause;
+ }
+ if (cause instanceof VirtualMachineError) {
+ throw (VirtualMachineError) cause;
+ }
+ }
+ logger.warn("Unable to create statement proxy for slow query report.", x);
+ }
+ return statement;
+ }
+
+ /**
+ * Creates a proxy for a Statement.
+ *
+ * @param proxy The proxy object on which the method that triggered
+ * the creation of the statement was called.
+ * @param method The method that was called on the proxy
+ * @param args The arguments passed as part of the method call to
+ * the proxy
+ * @param statement The statement object that is to be proxied
+ * @param constructor The constructor for the desired proxy
+ * @param sql The sql of of the statement
+ *
+ * @return A new proxy for the Statement
+ * @throws InstantiationException Couldn't instantiate object
+ * @throws IllegalAccessException Inaccessible constructor
+ * @throws InvocationTargetException Exception thrown from constructor
+ */
+ protected Object createDecorator(Object proxy, Method method, Object[] args,
+ Object statement, Constructor<?> constructor, String sql)
+ throws InstantiationException, IllegalAccessException, InvocationTargetException {
+ Object result = null;
+ StatementProxy<Statement> statementProxy =
+ new StatementProxy<>((Statement)statement,sql);
+ result = constructor.newInstance(new Object[] { statementProxy });
+ statementProxy.setActualProxy(result);
+ statementProxy.setConnection(proxy);
+ statementProxy.setConstructor(constructor);
+ return result;
+ }
+
+ protected boolean isExecuteQuery(String methodName) {
+ return EXECUTE_QUERY.equals(methodName);
+ }
+
+ protected boolean isExecuteQuery(Method method) {
+ return isExecuteQuery(method.getName());
+ }
+
+ protected boolean isResultSet(Method method, boolean process) {
+ return process(RESULTSET_TYPES, method, process);
+ }
+
+ /**
+ * Class to measure query execute time.
+ */
+ protected class StatementProxy<T extends java.sql.Statement> implements InvocationHandler {
+
+ protected boolean closed = false;
+ protected T delegate;
+ private Object actualProxy;
+ private Object connection;
+ private String sql;
+ private Constructor<?> constructor;
+
+ public StatementProxy(T delegate, String sql) {
+ this.delegate = delegate;
+ this.sql = sql;
+ }
+ public T getDelegate() {
+ return this.delegate;
+ }
+
+ public String getSql() {
+ return sql;
+ }
+
+ public void setConnection(Object proxy) {
+ this.connection = proxy;
+ }
+ public Object getConnection() {
+ return this.connection;
+ }
+
+ public void setActualProxy(Object proxy){
+ this.actualProxy = proxy;
+ }
+ public Object getActualProxy() {
+ return this.actualProxy;
+ }
+
+
+ public Constructor<?> getConstructor() {
+ return constructor;
+ }
+ public void setConstructor(Constructor<?> constructor) {
+ this.constructor = constructor;
+ }
+ public void closeInvoked() {
+ if (getDelegate()!=null) {
+ try {
+ getDelegate().close();
+ }catch (SQLException ignore) {
+ }
+ }
+ closed = true;
+ delegate = null;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (compare(TOSTRING_VAL,method)) {
+ return toString();
+ }
+ // was close invoked?
+ boolean close = compare(CLOSE_VAL, method);
+ // allow close to be called multiple times
+ if (close && closed)
+ return null;
+ // are we calling isClosed?
+ if (compare(ISCLOSED_VAL, method))
+ return Boolean.valueOf(closed);
+ // if we are calling anything else, bail out
+ if (closed)
+ throw new SQLException("Statement closed.");
+ if (compare(GETCONNECTION_VAL,method)){
+ return connection;
+ }
+ boolean process = false;
+ process = isResultSet(method, process);
+ // check to see if we are about to execute a query
+ // if we are executing, get the current time
+ Object result = null;
+ try {
+ // perform close cleanup
+ if (close) {
+ closeInvoked();
+ } else {
+ // execute the query
+ result = method.invoke(delegate, args);
+ }
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException
+ && t.getCause() != null) {
+ throw t.getCause();
+ } else {
+ throw t;
+ }
+ }
+ if (process && result != null) {
+ Constructor<?> cons = getResultSetConstructor();
+ result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)});
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(StatementProxy.class.getName());
+ buf.append("[Proxy=");
+ buf.append(System.identityHashCode(this));
+ buf.append("; Sql=");
+ buf.append(getSql());
+ buf.append("; Delegate=");
+ buf.append(getDelegate());
+ buf.append("; Connection=");
+ buf.append(getConnection());
+ buf.append("]");
+ return buf.toString();
+ }
+ }
+
+ protected class ResultSetProxy implements InvocationHandler {
+
+ private Object st;
+ private Object delegate;
+
+ public ResultSetProxy(Object st, Object delegate) {
+ this.st = st;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("getStatement")) {
+ return this.st;
+ } else {
+ try {
+ return method.invoke(this.delegate, args);
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException
+ && t.getCause() != null) {
+ throw t.getCause();
+ } else {
+ throw t;
+ }
+ }
+ }
+ }
+ }
+}