You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fh...@apache.org on 2010/02/24 20:27:09 UTC
svn commit: r915940 - in /tomcat/trunk/modules/jdbc-pool: doc/jdbc-pool.xml
java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
Author: fhanik
Date: Wed Feb 24 19:27:09 2010
New Revision: 915940
URL: http://svn.apache.org/viewvc?rev=915940&view=rev
Log:
https://issues.apache.org/bugzilla/show_bug.cgi?id=48392
Add an interceptor to be able to wrap existing statements and result sets to prevent code from accessing the connection
Patch submitted by gsfernandes@gmail.com
Added:
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java (with props)
Modified:
tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml
Modified: tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml?rev=915940&r1=915939&r2=915940&view=diff
==============================================================================
--- tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml (original)
+++ tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml Wed Feb 24 19:27:09 2010
@@ -472,6 +472,13 @@
<attributes>
</attributes>
</subsection>
+ <subsection name="org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor">
+ <p>See <bug>48392</bug>. Interceptor to wrap statements and result sets in order to prevent access to the actual connection
+ using the methods <code>ResultSet.getStatement().getConnection()</code> and <code>Statement.getConnection()</code>
+ </p>
+ <attributes>
+ </attributes>
+ </subsection>
<subsection name="org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport">
<p>Keeps track of query performance and issues log entries when queries exceed a time threshold of fail.
The log level used is <code>WARN</code>
Added: tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java?rev=915940&view=auto
==============================================================================
--- tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java (added)
+++ tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java Wed Feb 24 19:27:09 2010
@@ -0,0 +1,221 @@
+/*
+ * 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;
+import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
+import org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor;
+
+/**
+ * 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);
+
+ private static final String[] EXECUTE_QUERY_TYPES = { "executeQuery" };
+
+ /**
+ * 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
+ */
+ 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 {
+ Object result = null;
+ String name = method.getName();
+ Constructor<?> constructor = 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);
+ } else if (compare(PREPARE_CALL, name)) {
+ // prepareCall
+ constructor = getConstructor(PREPARE_IDX, CallableStatement.class);
+ } else {
+ // do nothing, might be a future unsupported method
+ // so we better bail out and let the system continue
+ return statement;
+ }
+ StatementProxy statementProxy = new StatementProxy(statement);
+ result = constructor.newInstance(new Object[] { statementProxy });
+ statementProxy.setActualProxy(result);
+ statementProxy.setConnection(proxy);
+ return result;
+ } catch (Exception x) {
+ logger.warn("Unable to create statement proxy for slow query report.", x);
+ }
+ return statement;
+ }
+
+ protected boolean isExecuteQuery(String methodName) {
+ return EXECUTE_QUERY_TYPES[0].equals(methodName);
+ }
+
+ protected boolean isExecuteQuery(Method method) {
+ return isExecuteQuery(method.getName());
+ }
+
+ /**
+ * Class to measure query execute time
+ *
+ * @author fhanik
+ *
+ */
+ protected class StatementProxy implements InvocationHandler {
+
+ protected boolean closed = false;
+ protected Object delegate;
+ private Object actualProxy;
+ private Object connection;
+
+ public StatementProxy(Object parent) {
+ this.delegate = parent;
+ }
+
+ public void setConnection(Object proxy) {
+ this.connection = proxy;
+ }
+
+ public void setActualProxy(Object proxy){
+ this.actualProxy = proxy;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ // get the name of the method for comparison
+ final String name = method.getName();
+ // was close invoked?
+ boolean close = compare(JdbcInterceptor.CLOSE_VAL, name);
+ // allow close to be called multiple times
+ if (close && closed)
+ return null;
+ // are we calling isClosed?
+ if (compare(JdbcInterceptor.ISCLOSED_VAL, name))
+ return Boolean.valueOf(closed);
+ // if we are calling anything else, bail out
+ if (closed)
+ throw new SQLException("Statement closed.");
+ if (name.equals("getConnection")){
+ return connection;
+ }
+ boolean process = isExecuteQuery(method);
+ // check to see if we are about to execute a query
+ // if we are executing, get the current time
+ Object result = null;
+ try {
+ // execute the query
+ result = method.invoke(delegate, args);
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException) {
+ InvocationTargetException it = (InvocationTargetException) t;
+ throw it.getCause() != null ? it.getCause() : it;
+ } else {
+ throw t;
+ }
+ }
+ // perform close cleanup
+ if (close) {
+ closed = true;
+ delegate = null;
+ }
+ if (process){
+ Constructor<?> cons = getResultSetConstructor();
+ result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)});
+ }
+ return result;
+ }
+ }
+
+ protected class ResultSetProxy implements InvocationHandler {
+
+ private Object st;
+ private Object delegate;
+
+ public ResultSetProxy(Object st, Object delegate) {
+ this.st = st;
+ this.delegate = delegate;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("getStatement")) {
+ return this.st;
+ } else {
+ return method.invoke(this.delegate, args);
+ }
+ }
+ }
+}
Propchange: tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org