diff --git a/src/main/java/com/zaxxer/hikari/HikariPool.java b/src/main/java/com/zaxxer/hikari/HikariPool.java index 65125790..a275f7e3 100644 --- a/src/main/java/com/zaxxer/hikari/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/HikariPool.java @@ -36,7 +36,7 @@ import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.zaxxer.hikari.proxy.HikariInstrumentationAgent; +import com.zaxxer.hikari.javassist.HikariInstrumentationAgent; import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.JavassistProxyFactoryFactory; import com.zaxxer.hikari.util.ClassLoaderUtils; @@ -82,7 +82,7 @@ public class HikariPool implements HikariPoolMBean PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties()); HikariInstrumentationAgent instrumentationAgent = new HikariInstrumentationAgent(dataSource); - if (true || !instrumentationAgent.loadTransformerAgent()) + if (false || !instrumentationAgent.loadTransformerAgent()) { delegationProxies = true; LOGGER.info("Falling back to Javassist delegate-based proxies."); @@ -261,6 +261,7 @@ public class HikariPool implements HikariPoolMBean else { proxyConnection = (IHikariConnectionProxy) connection; + proxyConnection.setParentPool(this); } boolean alive = isConnectionAlive((Connection) proxyConnection, configuration.getConnectionTimeout()); @@ -329,7 +330,7 @@ public class HikariPool implements HikariPoolMBean try { totalConnections.decrementAndGet(); - connectionProxy.getDelegate().close(); + connectionProxy.__close(); } catch (SQLException e) { diff --git a/src/main/java/com/zaxxer/hikari/proxy/CallableStatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/CallableStatementProxy.java index 058371e4..d78ff65a 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/CallableStatementProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/CallableStatementProxy.java @@ -17,87 +17,21 @@ package com.zaxxer.hikari.proxy; import java.sql.CallableStatement; -import java.sql.ResultSet; -import java.sql.SQLException; /** * * @author Brett Wooldridge */ -public class CallableStatementProxy extends HikariProxyBase +public class CallableStatementProxy extends PreparedStatementProxy { - private static final ProxyFactory PROXY_FACTORY; - - private final ConnectionProxy connection; - - protected final CallableStatement delegate; - - static - { - PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); - } - protected CallableStatementProxy(ConnectionProxy connection, CallableStatement statement) { - this.connection = connection; - this.delegate = statement; - } - - protected SQLException checkException(SQLException e) - { - return connection.checkException(e); + super(connection, statement); } // ********************************************************************** // Overridden java.sql.CallableStatement Methods - // other methods are injected // ********************************************************************** - - public void close() throws SQLException - { - if (delegate == null) - { - return; - } - - connection.unregisterStatement(delegate); - delegate.close(); - } - - public ResultSet executeQuery() throws SQLException - { - return PROXY_FACTORY.getProxyResultSet((CallableStatement) this, delegate.executeQuery()); - } - - public ResultSet executeQuery(String sql) throws SQLException - { - return PROXY_FACTORY.getProxyResultSet((CallableStatement) this, delegate.executeQuery(sql)); - } - - public ResultSet getGeneratedKeys() throws SQLException - { - return PROXY_FACTORY.getProxyResultSet((CallableStatement) this, delegate.getGeneratedKeys()); - } - - /* java.sql.Wrapper implementation */ // TODO implement wrapper -// public boolean isWrapperFor(Class iface) throws SQLException -// { -// return iface.isAssignableFrom(delegate.getClass()) || isWrapperFor(delegate, iface); -// } -// -// @SuppressWarnings("unchecked") -// public T unwrap(Class iface) throws SQLException -// { -// if (iface.isAssignableFrom(delegate.getClass())) -// { -// return (T) delegate; -// } -// if (isWrapperFor(iface)) -// { -// return unwrap(delegate, iface); -// } -// throw new SQLException(getClass().getName() + " is not a wrapper for " + iface); -// } } \ No newline at end of file diff --git a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index 5b9f2440..440dce43 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -28,36 +28,44 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; -import org.slf4j.LoggerFactory; - import com.zaxxer.hikari.HikariPool; +import com.zaxxer.hikari.javassist.HikariInject; /** + * This is the proxy class for java.sql.Connection. It is used in + * two ways: + * + * 1) If instrumentation is not used, Javassist will generate a new class + * that extends this class and delegates all method calls to the 'delegate' + * member (which points to the real Connection). + * + * 2) If instrumentation IS used, Javassist will be used to inject all of + * the non-final methods of this class into the actual Connection implementation + * provided by the JDBC driver. All of the fields, except for PROXY_FACTORY + * and 'delegate' are also injected. In order to avoid name conflicts the + * fields of this class have slightly unconventional names. * * @author Brett Wooldridge */ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectionProxy { - private static final ProxyFactory PROXY_FACTORY; - - private static final Set POSTGRESQL_ERRORS; - private static final Set SPECIAL_ERRORS; + private static ProxyFactory PROXY_FACTORY; - private final ArrayList openStatements; - private final AtomicBoolean isClosed; + @HikariInject private static final Set POSTGRESQL_ERRORS; + @HikariInject private static final Set SPECIAL_ERRORS; - private final HikariPool parentPool; + @HikariInject private ArrayList _openStatements; + @HikariInject private AtomicBoolean _isClosed; + @HikariInject private HikariPool _parentPool; protected final Connection delegate; - private volatile boolean forceClose; - - private final long creationTime; - private long lastAccess; - - private StackTraceElement[] stackTrace; + @HikariInject private volatile boolean _forceClose; + @HikariInject private long _creationTime; + @HikariInject private long _lastAccess; - private TimerTask leakTask; + @HikariInject private StackTraceElement[] _stackTrace; + @HikariInject private TimerTask _leakTask; // static initializer static @@ -71,88 +79,90 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio SPECIAL_ERRORS = new HashSet(); SPECIAL_ERRORS.add("01002"); // SQL92 disconnect error - PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); + __static(); } - // Instance initializer + @HikariInject + private void __init() { - openStatements = new ArrayList(64); - isClosed = new AtomicBoolean(); - creationTime = lastAccess = System.currentTimeMillis(); + _openStatements = new ArrayList(64); + _isClosed = new AtomicBoolean(); + _creationTime = _lastAccess = System.currentTimeMillis(); } protected ConnectionProxy(HikariPool parentPool, Connection connection) { - this.parentPool = parentPool; + this._parentPool = parentPool; this.delegate = connection; + __init(); } - void unregisterStatement(Object statement) + @HikariInject + public void unregisterStatement(Object statement) { // If the connection is not closed. If it is closed, it means this is being // called back as a result of the close() method below in which case we // will clear the openStatements collection en mass. - if (!isClosed.get()) + if (!_isClosed.get()) { - openStatements.remove(statement); + _openStatements.remove(statement); } } + @HikariInject public long getCreationTime() { - return creationTime; + return _creationTime; } + @HikariInject public long getLastAccess() { - return lastAccess; + return _lastAccess; } + @HikariInject public void setLastAccess(long timestamp) { - this.lastAccess = timestamp; + this._lastAccess = timestamp; } + @HikariInject + public void setParentPool(HikariPool parentPool) + { + this._parentPool = parentPool; + } + + @HikariInject public void unclose() { - isClosed.set(false); + _isClosed.set(false); } - public Connection getDelegate() + public final Connection getDelegate() { return delegate; } + @HikariInject public void captureStack(long leakDetectionThreshold, Timer scheduler) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - stackTrace = new StackTraceElement[trace.length - 4]; - System.arraycopy(trace, 4, stackTrace, 0, stackTrace.length); + _stackTrace = new StackTraceElement[trace.length - 4]; + System.arraycopy(trace, 4, _stackTrace, 0, _stackTrace.length); - final long leakTime = System.currentTimeMillis() + leakDetectionThreshold; - leakTask = new TimerTask() - { - public void run() - { - if (System.currentTimeMillis() > leakTime) - { - Exception e = new Exception(); - e.setStackTrace(stackTrace); - LoggerFactory.getLogger(ConnectionProxy.this.getClass()).warn("Connection leak detection triggered, stack trace follows", e); - stackTrace = null; - } - } - }; - - scheduler.schedule(leakTask, leakDetectionThreshold); + _leakTask = new LeakTask(_stackTrace, leakDetectionThreshold); + scheduler.schedule(_leakTask, leakDetectionThreshold); } + @HikariInject public boolean isBrokenConnection() { - return forceClose; + return _forceClose; } - - protected SQLException checkException(SQLException sqle) + + @HikariInject + public SQLException checkException(SQLException sqle) { String sqlState = sqle.getSQLState(); if (sqlState == null) @@ -163,42 +173,39 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio sqlState = sqlState.toUpperCase(); if (sqlState.startsWith("08")) { - forceClose = true; + _forceClose = true; } else if (POSTGRESQL_ERRORS.contains(sqlState.toUpperCase()) || SPECIAL_ERRORS.contains(sqlState)) { - forceClose = true; + _forceClose = true; } return sqle; } // ********************************************************************** - // Overridden java.sql.Connection Methods - // other methods are injected + // "Overridden" java.sql.Connection Methods // ********************************************************************** - /* (non-Javadoc) - * @see java.sql.Connection#close() - */ + @HikariInject public void close() throws SQLException { - if (isClosed.compareAndSet(false, true)) + if (_isClosed.compareAndSet(false, true)) { - if (leakTask != null) + if (_leakTask != null) { - leakTask.cancel(); - leakTask = null; + _leakTask.cancel(); + _leakTask = null; } - if (delegate.getAutoCommit()) + if (getAutoCommit()) { - delegate.commit(); + commit(); } try { - for (Statement statement : openStatements) + for (IHikariStatementProxy statement : _openStatements) { statement.close(); } @@ -209,31 +216,28 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } finally { - openStatements.clear(); - parentPool.releaseConnection(this); + _openStatements.clear(); + _parentPool.releaseConnection(this); } } } - /* (non-Javadoc) - * @see java.sql.Connection#isClosed() - */ + @HikariInject public boolean isClosed() throws SQLException { - return isClosed.get(); + return _isClosed.get(); } - /* (non-Javadoc) - * @see java.sql.Connection#createStatement() - */ + @HikariInject public Statement createStatement() throws SQLException { try { - Statement statementProxy = PROXY_FACTORY.getProxyStatement(this, delegate.createStatement()); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __createStatement(); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (Statement) statementProxy; } catch (SQLException e) { @@ -241,17 +245,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#createStatement(int, int) - */ + @HikariInject public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { try { - Statement statementProxy = PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __createStatement(resultSetType, resultSetConcurrency); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (Statement) statementProxy; } catch (SQLException e) { @@ -259,17 +262,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#createStatement(int, int, int) - */ + @HikariInject public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { try { - Statement statementProxy = PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (Statement) statementProxy; } catch (SQLException e) { @@ -277,17 +279,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareCall(java.lang.String) - */ + @HikariInject public CallableStatement prepareCall(String sql) throws SQLException { try { - CallableStatement statementProxy = PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareCall(sql); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (CallableStatement) statementProxy; } catch (SQLException e) { @@ -295,17 +296,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareCall(java.lang.String, int, int) - */ + @HikariInject public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { try { - CallableStatement statementProxy = PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareCall(sql, resultSetType, resultSetConcurrency); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (CallableStatement) statementProxy; } catch (SQLException e) { @@ -313,17 +313,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int) - */ + @HikariInject public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { try { - CallableStatement statementProxy = PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (CallableStatement) statementProxy; } catch (SQLException e) { @@ -331,17 +330,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String) - */ + @HikariInject public PreparedStatement prepareStatement(String sql) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { @@ -349,17 +347,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String, int) - */ + @HikariInject public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql, autoGeneratedKeys); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { @@ -367,17 +364,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String, int, int) - */ + @HikariInject public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql, resultSetType, resultSetConcurrency); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { @@ -385,17 +381,15 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String, int, int, int) - */ + @HikariInject public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { @@ -403,17 +397,16 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String, int[]) - */ + @HikariInject public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql, columnIndexes); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { @@ -421,21 +414,110 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio } } - /* (non-Javadoc) - * @see java.sql.Connection#prepareStatement(java.lang.String, java.lang.String[]) - */ + @HikariInject public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { try { - PreparedStatement statementProxy = PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames)); - openStatements.add(statementProxy); + IHikariStatementProxy statementProxy = (IHikariStatementProxy) __prepareStatement(sql, columnNames); + statementProxy.setConnectionProxy((Connection) this); + _openStatements.add(statementProxy); - return statementProxy; + return (PreparedStatement) statementProxy; } catch (SQLException e) { throw checkException(e); } } + + public boolean getAutoCommit() throws SQLException + { + return delegate.getAutoCommit(); + } + + public void commit() throws SQLException + { + delegate.commit(); + } + + // *********************************************************************** + // These methods contain code we do not want injected into the actual + // java.sql.Connection implementation class. These methods are only + // used when instrumentation is not available and "conventional" Javassist + // delegating proxies are used. + // *********************************************************************** + + private static void __static() + { + if (PROXY_FACTORY == null) + { + PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); + } + } + + public final void __close() throws SQLException + { + delegate.close(); + } + + public final Statement __createStatement() throws SQLException + { + return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement()); + } + + public final Statement __createStatement(int resultSetType, int resultSetConcurrency) throws SQLException + { + return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency)); + } + + public final Statement __createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException + { + return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + public final CallableStatement __prepareCall(String sql) throws SQLException + { + return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql)); + } + + public final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException + { + return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); + } + + public final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException + { + return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + public final PreparedStatement __prepareStatement(String sql) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql)); + } + + public final PreparedStatement __prepareStatement(String sql, int autoGeneratedKeys) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys)); + } + + public final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); + } + + public final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + public final PreparedStatement __prepareStatement(String sql, int[] columnIndexes) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes)); + } + + public final PreparedStatement __prepareStatement(String sql, String[] columnNames) throws SQLException + { + return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames)); + } } diff --git a/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java index 9e1f5a74..6058d1f6 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java @@ -17,8 +17,11 @@ package com.zaxxer.hikari.proxy; import java.sql.Connection; +import java.sql.SQLException; import java.util.Timer; +import com.zaxxer.hikari.HikariPool; + /** * * @author Brett Wooldridge @@ -27,6 +30,12 @@ public interface IHikariConnectionProxy { void unclose(); + void __close() throws SQLException; + + void unregisterStatement(Object statement); + + SQLException checkException(SQLException sqle); + boolean isBrokenConnection(); long getCreationTime(); @@ -35,6 +44,8 @@ public interface IHikariConnectionProxy void setLastAccess(long timestamp); + void setParentPool(HikariPool parentPool); + Connection getDelegate(); /* Leak Detection API */ diff --git a/src/main/java/com/zaxxer/hikari/proxy/IHikariResultSetProxy.java b/src/main/java/com/zaxxer/hikari/proxy/IHikariResultSetProxy.java new file mode 100644 index 00000000..b663d5df --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/proxy/IHikariResultSetProxy.java @@ -0,0 +1,6 @@ +package com.zaxxer.hikari.proxy; + +public interface IHikariResultSetProxy +{ + void setProxyStatement(IHikariStatementProxy proxy); +} diff --git a/src/main/java/com/zaxxer/hikari/proxy/IHikariStatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/IHikariStatementProxy.java new file mode 100644 index 00000000..7a4bc6ad --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/proxy/IHikariStatementProxy.java @@ -0,0 +1,13 @@ +package com.zaxxer.hikari.proxy; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface IHikariStatementProxy +{ + void close() throws SQLException; + + void setConnectionProxy(Connection connectionProxy); + + SQLException checkException(SQLException e); +} diff --git a/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactoryFactory.java b/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactoryFactory.java index 7d3bfe59..b10e1c25 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactoryFactory.java +++ b/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactoryFactory.java @@ -40,7 +40,7 @@ public final class JavassistProxyFactoryFactory { private static final ProxyFactory proxyFactory; - private static ClassPool classPool; + private ClassPool classPool; static { @@ -178,7 +178,7 @@ public final class JavassistProxyFactoryFactory targetCt.addMethod(method); } } - + targetCt.debugWriteFile("/tmp"); return targetCt.toClass(classPool.getClassLoader(), null); } } diff --git a/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java b/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java new file mode 100644 index 00000000..4c63a6e0 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Brett Wooldridge + * + * 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.zaxxer.hikari.proxy; + +import java.util.TimerTask; + +import org.slf4j.LoggerFactory; + +/** + * @author Brett Wooldridge + */ +public class LeakTask extends TimerTask +{ + private final long leakTime; + private StackTraceElement[] stackTrace; + + public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold) + { + this.stackTrace = stackTrace; + this.leakTime = System.currentTimeMillis() + leakDetectionThreshold; + } + + /** {@inheritDoc} */ + @Override + public void run() + { + if (System.currentTimeMillis() > leakTime) + { + Exception e = new Exception(); + e.setStackTrace(stackTrace); + LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); + stackTrace = null; + } + } + + @Override + public boolean cancel() + { + boolean cancelled = super.cancel(); + if (cancelled) + { + stackTrace = null; + } + return cancelled; + } +} diff --git a/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java index 032f1534..5ae36a32 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java @@ -20,109 +20,47 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import com.zaxxer.hikari.javassist.HikariInject; + /** * * @author Brett Wooldridge */ -public class PreparedStatementProxy extends HikariProxyBase +public class PreparedStatementProxy extends StatementProxy { - private static final ProxyFactory PROXY_FACTORY; - - private final ConnectionProxy connection; - - protected final PreparedStatement delegate; - - static - { - PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); - } - protected PreparedStatementProxy(ConnectionProxy connection, PreparedStatement statement) { - this.connection = connection; - this.delegate = statement; - } - - protected SQLException checkException(SQLException e) - { - return connection.checkException(e); + super(connection, statement); } // ********************************************************************** // Overridden java.sql.PreparedStatement Methods - // other methods are injected // ********************************************************************** - public void close() throws SQLException - { - if (delegate == null) - { - return; - } - - connection.unregisterStatement(this); - delegate.close(); - } - - public ResultSet getResultSet() throws SQLException - { - ResultSet resultSet = delegate.getResultSet(); - if (resultSet == null) - { - return null; - } - return PROXY_FACTORY.getProxyResultSet((PreparedStatement) this, resultSet); - } - + @HikariInject public ResultSet executeQuery() throws SQLException { - ResultSet resultSet = delegate.executeQuery(); - if (resultSet == null) - { - return null; - } - return PROXY_FACTORY.getProxyResultSet((PreparedStatement) this, resultSet); + IHikariResultSetProxy resultSet = (IHikariResultSetProxy) __executeQuery(); + resultSet.setProxyStatement(this); + return (ResultSet) resultSet; } - public ResultSet executeQuery(String sql) throws SQLException - { - ResultSet resultSet = delegate.executeQuery(sql); - if (resultSet == null) - { - return null; - } - return PROXY_FACTORY.getProxyResultSet((PreparedStatement) this, resultSet); - } + // *********************************************************************** + // These methods contain code we do not want injected into the actual + // java.sql.Connection implementation class. These methods are only + // used when instrumentation is not available and "conventional" Javassist + // delegating proxies are used. + // *********************************************************************** - public ResultSet getGeneratedKeys() throws SQLException + public ResultSet __executeQuery() throws SQLException { - ResultSet generatedKeys = delegate.getGeneratedKeys(); - if (generatedKeys == null) + ResultSet resultSet = ((PreparedStatement) delegate).executeQuery(); + if (resultSet == null) { return null; } - return PROXY_FACTORY.getProxyResultSet((PreparedStatement) this, generatedKeys); + return PROXY_FACTORY.getProxyResultSet(this, resultSet); } - /* java.sql.Wrapper implementation */ - // TODO: fix wrapper -// public boolean isWrapperFor(Class iface) throws SQLException -// { -// return iface.isAssignableFrom(delegate.getClass()) || isWrapperFor(delegate, iface); -// } -// -// @SuppressWarnings("unchecked") -// public T unwrap(Class iface) throws SQLException -// { -// if (iface.isAssignableFrom(delegate.getClass())) -// { -// return (T) delegate; -// } -// if (isWrapperFor(iface)) -// { -// return unwrap(delegate, iface); -// } -// throw new SQLException(getClass().getName() + " is not a wrapper for " + iface); -// } } \ No newline at end of file diff --git a/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java b/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java index 4fd0b51a..5eb1b217 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java @@ -37,7 +37,7 @@ public abstract class ProxyFactory public abstract PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement); - public abstract ResultSet getProxyResultSet(Statement statement, ResultSet resultSet); + public abstract ResultSet getProxyResultSet(IHikariStatementProxy statement, ResultSet resultSet); /************************************************************************** * diff --git a/src/main/java/com/zaxxer/hikari/proxy/ResultSetProxy.java b/src/main/java/com/zaxxer/hikari/proxy/ResultSetProxy.java index f0a8f2d5..1f09cdd4 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ResultSetProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ResultSetProxy.java @@ -20,24 +20,33 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import com.zaxxer.hikari.javassist.HikariInject; + /** * @author Brett Wooldridge */ -public class ResultSetProxy extends HikariProxyBase +public class ResultSetProxy extends HikariProxyBase implements IHikariResultSetProxy { - private final Statement statement; + @HikariInject private IHikariStatementProxy _statement; protected final ResultSet delegate; - protected ResultSetProxy(Statement statement, ResultSet resultSet) + protected ResultSetProxy(IHikariStatementProxy statement, ResultSet resultSet) { - this.statement = statement; + this._statement = statement; this.delegate = resultSet; } - protected SQLException checkException(SQLException e) + @HikariInject + public SQLException checkException(SQLException e) + { + return _statement.checkException(e); + } + + @HikariInject + public void setProxyStatement(IHikariStatementProxy statement) { - return ((HikariProxyBase) statement).checkException(e); + this._statement = statement; } // ********************************************************************** @@ -47,6 +56,6 @@ public class ResultSetProxy extends HikariProxyBase public Statement getStatement() throws SQLException { - return statement; + return (Statement) _statement; } } \ No newline at end of file diff --git a/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java index ca6cc1bf..5b8785b5 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java @@ -16,55 +16,100 @@ package com.zaxxer.hikari.proxy; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import com.zaxxer.hikari.javassist.HikariInject; + /** * @author Brett Wooldridge */ -public class StatementProxy extends HikariProxyBase +public class StatementProxy extends HikariProxyBase implements IHikariStatementProxy { - private static final ProxyFactory PROXY_FACTORY; + protected static ProxyFactory PROXY_FACTORY; - protected final ConnectionProxy connection; + @HikariInject protected IHikariConnectionProxy _connection; - protected final Statement delegate; + protected Statement delegate; static { - PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); + __static(); } - protected StatementProxy(ConnectionProxy connection, Statement statement) + protected StatementProxy(IHikariConnectionProxy connection, Statement statement) { - this.connection = connection; + this._connection = connection; this.delegate = statement; } - protected SQLException checkException(SQLException e) + @HikariInject + public void setConnectionProxy(Connection connection) + { + this._connection = (IHikariConnectionProxy) connection; + } + + @HikariInject + public SQLException checkException(SQLException e) { - return connection.checkException(e); + return _connection.checkException(e); } // ********************************************************************** // Overridden java.sql.Statement Methods - // other methods are injected // ********************************************************************** + @HikariInject public void close() throws SQLException { - connection.unregisterStatement(this); + _connection.unregisterStatement(this); + __close(); + } + + public ResultSet executeQuery(String sql) throws SQLException + { + IHikariResultSetProxy resultSet = (IHikariResultSetProxy) __executeQuery(sql); + resultSet.setProxyStatement(this); + + return (ResultSet) resultSet; + } + + public ResultSet getGeneratedKeys() throws SQLException + { + IHikariResultSetProxy resultSet = (IHikariResultSetProxy) __getGeneratedKeys(); + resultSet.setProxyStatement(this); + return (ResultSet) resultSet; + } + + // *********************************************************************** + // These methods contain code we do not want injected into the actual + // java.sql.Connection implementation class. These methods are only + // used when instrumentation is not available and "conventional" Javassist + // delegating proxies are used. + // *********************************************************************** + + private static void __static() + { + if (PROXY_FACTORY == null) + { + PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory(); + } + } + + public void __close() throws SQLException + { if (delegate.isClosed()) { return; } - delegate.close(); + delegate.close(); } - public ResultSet executeQuery(String sql) throws SQLException + public ResultSet __executeQuery(String sql) throws SQLException { ResultSet resultSet = delegate.executeQuery(sql); if (resultSet == null) @@ -72,10 +117,10 @@ public class StatementProxy extends HikariProxyBase return null; } - return PROXY_FACTORY.getProxyResultSet((Statement) this, resultSet); + return PROXY_FACTORY.getProxyResultSet(this, resultSet); } - public ResultSet getGeneratedKeys() throws SQLException + public ResultSet __getGeneratedKeys() throws SQLException { ResultSet generatedKeys = delegate.getGeneratedKeys(); if (generatedKeys == null) @@ -83,28 +128,8 @@ public class StatementProxy extends HikariProxyBase return null; } - return PROXY_FACTORY.getProxyResultSet((Statement) this, generatedKeys); + return PROXY_FACTORY.getProxyResultSet(this, generatedKeys); } - /* java.sql.Wrapper implementation */ - // TODO: fix wrapper -// public boolean isWrapperFor(Class iface) throws SQLException -// { -// return iface.isAssignableFrom(delegate.getClass()) || isWrapperFor(delegate, iface); -// } -// -// @SuppressWarnings("unchecked") -// public T unwrap(Class iface) throws SQLException -// { -// if (iface.isAssignableFrom(delegate.getClass())) -// { -// return (T) delegate; -// } -// if (isWrapperFor(iface)) -// { -// return unwrap(delegate, iface); -// } -// throw new SQLException(getClass().getName() + " is not a wrapper for " + iface); -// } } \ No newline at end of file diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java index 0a55b138..b948b174 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java @@ -41,6 +41,13 @@ import java.util.concurrent.Executor; */ public class StubConnection implements Connection { + private static long foo; + + static + { + foo = System.currentTimeMillis(); + } + /** {@inheritDoc} */ public T unwrap(Class iface) throws SQLException { @@ -200,7 +207,7 @@ public class StubConnection implements Connection /** {@inheritDoc} */ public int getHoldability() throws SQLException { - return 0; + return (int) foo; } /** {@inheritDoc} */ diff --git a/src/test/java/com/zaxxer/hikari/performance/Benchmark1.java b/src/test/java/com/zaxxer/hikari/performance/Benchmark1.java index aefa8405..240eaa16 100644 --- a/src/test/java/com/zaxxer/hikari/performance/Benchmark1.java +++ b/src/test/java/com/zaxxer/hikari/performance/Benchmark1.java @@ -84,7 +84,7 @@ public class Benchmark1 config.setIdleTimeout(TimeUnit.MINUTES.toMillis(30)); config.setJdbc4ConnectionTest(true); config.setPoolName("This has spaces"); - config.setDataSourceClassName("com.zaxxer.hikari.performance.StubDataSource"); + config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); HikariDataSource ds = new HikariDataSource(config); return ds;