Replace ConnectionProxy’s ArrayList<Statement> with a custom class (FastStatementList), replace the singleton generated JavassistProxyFactory with a class that has static methods (so we get invokestatic rather than invokeinterface) in bytecode.

pull/30/head
Brett Wooldridge 11 years ago
parent 4a6eb29043
commit 04aa65a6d8

@ -29,6 +29,7 @@ import javax.sql.DataSource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.proxy.JavassistProxyFactory;
import com.zaxxer.hikari.util.PropertyBeanSetter; import com.zaxxer.hikari.util.PropertyBeanSetter;
public final class HikariConfig implements HikariConfigMBean public final class HikariConfig implements HikariConfigMBean
@ -64,6 +65,11 @@ public final class HikariConfig implements HikariConfigMBean
private Properties dataSourceProperties; private Properties dataSourceProperties;
private DataSource dataSource; private DataSource dataSource;
static
{
JavassistProxyFactory.initialize();
}
/** /**
* Default constructor * Default constructor
*/ */

@ -32,7 +32,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.JavassistProxyFactoryFactory; import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.PropertyBeanSetter; import com.zaxxer.hikari.util.PropertyBeanSetter;
/** /**
@ -325,7 +325,7 @@ public final class HikariPool implements HikariPoolMBean
try try
{ {
Connection connection = dataSource.getConnection(); Connection connection = dataSource.getConnection();
IHikariConnectionProxy proxyConnection = (IHikariConnectionProxy) JavassistProxyFactoryFactory.getProxyFactory().getProxyConnection(this, connection); IHikariConnectionProxy proxyConnection = (IHikariConnectionProxy) ProxyFactory.getProxyConnection(this, connection);
if (transactionIsolation < 0) if (transactionIsolation < 0)
{ {

@ -27,7 +27,7 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import com.zaxxer.hikari.HikariPool; import com.zaxxer.hikari.HikariPool;
import com.zaxxer.hikari.util.FastList; import com.zaxxer.hikari.util.FastStatementList;
/** /**
* This is the proxy class for java.sql.Connection. * This is the proxy class for java.sql.Connection.
@ -36,13 +36,11 @@ import com.zaxxer.hikari.util.FastList;
*/ */
public abstract class ConnectionProxy implements IHikariConnectionProxy public abstract class ConnectionProxy implements IHikariConnectionProxy
{ {
private static final ProxyFactory PROXY_FACTORY;
private static final Set<String> SQL_ERRORS; private static final Set<String> SQL_ERRORS;
protected final Connection delegate; protected final Connection delegate;
private final FastList<Statement> openStatements; private final FastStatementList openStatements;
private final HikariPool parentPool; private final HikariPool parentPool;
private boolean isClosed; private boolean isClosed;
@ -63,8 +61,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
SQL_ERRORS.add("57P03"); // CANNOT CONNECT NOW SQL_ERRORS.add("57P03"); // CANNOT CONNECT NOW
SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN
SQL_ERRORS.add("01002"); // SQL92 disconnect error SQL_ERRORS.add("01002"); // SQL92 disconnect error
PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory();
} }
protected ConnectionProxy(HikariPool pool, Connection connection) protected ConnectionProxy(HikariPool pool, Connection connection)
@ -73,7 +69,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
this.delegate = connection; this.delegate = connection;
creationTime = lastAccess = System.currentTimeMillis(); creationTime = lastAccess = System.currentTimeMillis();
openStatements = new FastList<Statement>(); openStatements = new FastStatementList();
} }
public final void unregisterStatement(Object statement) public final void unregisterStatement(Object statement)
@ -409,61 +405,61 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
private final Statement __createStatement() throws SQLException private final Statement __createStatement() throws SQLException
{ {
return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement()); return ProxyFactory.getProxyStatement(this, delegate.createStatement());
} }
private final Statement __createStatement(int resultSetType, int resultSetConcurrency) throws SQLException private final Statement __createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{ {
return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency)); return ProxyFactory.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency));
} }
private final Statement __createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException private final Statement __createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{ {
return PROXY_FACTORY.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); return ProxyFactory.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
} }
private final CallableStatement __prepareCall(String sql) throws SQLException private final CallableStatement __prepareCall(String sql) throws SQLException
{ {
return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql)); return ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql));
} }
private final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException private final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{ {
return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); return ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency));
} }
private final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException private final CallableStatement __prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{ {
return PROXY_FACTORY.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); return ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} }
private final PreparedStatement __prepareStatement(String sql) throws SQLException private final PreparedStatement __prepareStatement(String sql) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql));
} }
private final PreparedStatement __prepareStatement(String sql, int autoGeneratedKeys) throws SQLException private final PreparedStatement __prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys));
} }
private final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException private final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency));
} }
private final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException private final PreparedStatement __prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} }
private final PreparedStatement __prepareStatement(String sql, int[] columnIndexes) throws SQLException private final PreparedStatement __prepareStatement(String sql, int[] columnIndexes) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes));
} }
private final PreparedStatement __prepareStatement(String sql, String[] columnNames) throws SQLException private final PreparedStatement __prepareStatement(String sql, String[] columnNames) throws SQLException
{ {
return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames)); return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
} }
} }

@ -31,16 +31,16 @@ import javassist.CtNewMethod;
import javassist.LoaderClassPath; import javassist.LoaderClassPath;
import javassist.Modifier; import javassist.Modifier;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.util.ClassLoaderUtils; import com.zaxxer.hikari.util.ClassLoaderUtils;
/** /**
* *
* @author Brett Wooldridge * @author Brett Wooldridge
*/ */
public final class JavassistProxyFactoryFactory public final class JavassistProxyFactory
{ {
private static final ProxyFactory proxyFactory;
private ClassPool classPool; private ClassPool classPool;
static static
@ -48,11 +48,10 @@ public final class JavassistProxyFactoryFactory
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try try
{ {
Thread.currentThread().setContextClassLoader(JavassistProxyFactoryFactory.class.getClassLoader()); Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader());
JavassistProxyFactoryFactory proxyFactoryFactory = new JavassistProxyFactoryFactory();
proxyFactory = proxyFactoryFactory.generateProxyFactory(); JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory();
proxyFactoryFactory.modifyProxyFactory();
} }
catch (Exception e) catch (Exception e)
{ {
@ -64,7 +63,12 @@ public final class JavassistProxyFactoryFactory
} }
} }
private JavassistProxyFactoryFactory() public static void initialize()
{
// simply invoking this method causes the initialization of this class.
}
private JavassistProxyFactory()
{ {
classPool = new ClassPool(); classPool = new ClassPool();
classPool.importPackage("java.sql"); classPool.importPackage("java.sql");
@ -89,51 +93,43 @@ public final class JavassistProxyFactoryFactory
} }
} }
public static ProxyFactory getProxyFactory() private void modifyProxyFactory() throws Exception
{ {
return proxyFactory; String packageName = JavassistProxyFactory.class.getPackage().getName();
} CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.proxy.ProxyFactory");
for (CtMethod method : proxyCt.getMethods())
private ProxyFactory generateProxyFactory() throws Exception
{
String packageName = ProxyFactory.class.getPackage().getName();
CtClass targetCt = classPool.makeClass(packageName + ".JavassistProxyFactory");
CtClass superCt = classPool.getCtClass(ProxyFactory.class.getName());
targetCt.setSuperclass(superCt);
targetCt.setModifiers(Modifier.FINAL);
for (CtMethod intfMethod : superCt.getDeclaredMethods())
{ {
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
StringBuilder call = new StringBuilder("{"); StringBuilder call = new StringBuilder("{");
if ("getProxyConnection".equals(method.getName())) if ("getProxyConnection".equals(method.getName()))
{ {
call.append("return new ").append(packageName).append(".ConnectionJavassistProxy($$);"); call.append("return new ").append(packageName).append(".ConnectionJavassistProxy($$);");
} }
if ("getProxyStatement".equals(method.getName())) else if ("getProxyStatement".equals(method.getName()))
{ {
call.append("return new ").append(packageName).append(".StatementJavassistProxy($$);"); call.append("return new ").append(packageName).append(".StatementJavassistProxy($$);");
} }
if ("getProxyPreparedStatement".equals(method.getName())) else if ("getProxyPreparedStatement".equals(method.getName()))
{ {
call.append("return new ").append(packageName).append(".PreparedStatementJavassistProxy($$);"); call.append("return new ").append(packageName).append(".PreparedStatementJavassistProxy($$);");
} }
if ("getProxyResultSet".equals(method.getName())) else if ("getProxyResultSet".equals(method.getName()))
{ {
call.append("return $2 != null ? new ").append(packageName).append(".ResultSetJavassistProxy($$) : null;"); call.append("return $2 != null ? new ").append(packageName).append(".ResultSetJavassistProxy($$) : null;");
} }
if ("getProxyCallableStatement".equals(method.getName())) else if ("getProxyCallableStatement".equals(method.getName()))
{ {
call.append("return new ").append(packageName).append(".CallableStatementJavassistProxy($$);"); call.append("return new ").append(packageName).append(".CallableStatementJavassistProxy($$);");
} }
else
{
continue;
}
call.append('}'); call.append('}');
method.setBody(call.toString()); method.setBody(call.toString());
targetCt.addMethod(method);
} }
Class<?> clazz = targetCt.toClass(classPool.getClassLoader(), null); proxyCt.toClass(classPool.getClassLoader(), null);
return (ProxyFactory) clazz.newInstance();
} }
/** /**
@ -198,6 +194,11 @@ public final class JavassistProxyFactoryFactory
} }
} }
if (LoggerFactory.getLogger(getClass()).isDebugEnabled())
{
targetCt.debugWriteFile(System.getProperty("java.io.tmpdir"));
}
return targetCt.toClass(classPool.getClassLoader(), null); return targetCt.toClass(classPool.getClassLoader(), null);
} }
} }

@ -25,20 +25,39 @@ import java.sql.Statement;
import com.zaxxer.hikari.HikariPool; import com.zaxxer.hikari.HikariPool;
/** /**
* This class defines the interface for generating proxies, the * Injected proxy factory class.
* real (concrete) class is actually generated by Javassist.
* *
* @author Brett Wooldridge * @author Brett Wooldridge
*/ */
public abstract class ProxyFactory public final class ProxyFactory
{ {
public abstract Connection getProxyConnection(HikariPool pool, Connection connection); public static Connection getProxyConnection(HikariPool pool, Connection connection)
{
public abstract Statement getProxyStatement(ConnectionProxy connection, Statement statement); // Body is injected by JavassistProxyFactory
return null;
public abstract CallableStatement getProxyCallableStatement(ConnectionProxy connection, CallableStatement statement); }
public abstract PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement); static Statement getProxyStatement(ConnectionProxy connection, Statement statement)
{
public abstract ResultSet getProxyResultSet(StatementProxy statement, ResultSet resultSet); // Body is injected by JavassistProxyFactory
return null;
}
static CallableStatement getProxyCallableStatement(ConnectionProxy connection, CallableStatement statement)
{
// Body is injected by JavassistProxyFactory
return null;
}
static PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement)
{
// Body is injected by JavassistProxyFactory
return null;
}
static ResultSet getProxyResultSet(StatementProxy statement, ResultSet resultSet)
{
// Body is injected by JavassistProxyFactory
return null;
}
} }

@ -28,18 +28,11 @@ import java.sql.Statement;
*/ */
public abstract class StatementProxy implements Statement public abstract class StatementProxy implements Statement
{ {
protected static final ProxyFactory PROXY_FACTORY;
protected final IHikariConnectionProxy connection; protected final IHikariConnectionProxy connection;
protected final Statement delegate; protected final Statement delegate;
private boolean isClosed; private boolean isClosed;
static
{
PROXY_FACTORY = JavassistProxyFactoryFactory.getProxyFactory();
}
protected StatementProxy(IHikariConnectionProxy connection, Statement statement) protected StatementProxy(IHikariConnectionProxy connection, Statement statement)
{ {
this.connection = connection; this.connection = connection;
@ -55,7 +48,7 @@ public abstract class StatementProxy implements Statement
{ {
if (resultSet != null) if (resultSet != null)
{ {
return PROXY_FACTORY.getProxyResultSet(this, resultSet); return ProxyFactory.getProxyResultSet(this, resultSet);
} }
return null; return null;

@ -16,24 +16,26 @@
package com.zaxxer.hikari.util; package com.zaxxer.hikari.util;
import java.sql.Statement;
/** /**
* Fast list without range checking. * Fast list without range checking.
* *
* @author Brett Wooldridge * @author Brett Wooldridge
*/ */
public class FastList<E> public final class FastStatementList
{ {
private transient Object[] elementData; private Statement[] elementData;
private int size; private int size;
/** /**
* Construct a FastList with a default size of 16. * Construct a FastList with a default size of 16.
*/ */
public FastList() public FastStatementList()
{ {
this.elementData = new Object[16]; this.elementData = new Statement[16];
} }
/** /**
@ -41,9 +43,9 @@ public class FastList<E>
* *
* @param size the initial size of the FastList * @param size the initial size of the FastList
*/ */
public FastList(int size) public FastStatementList(int size)
{ {
this.elementData = new Object[size]; this.elementData = new Statement[size];
} }
/** /**
@ -51,7 +53,7 @@ public class FastList<E>
* *
* @param element the element to add * @param element the element to add
*/ */
public void add(E element) public void add(Statement element)
{ {
if (size < elementData.length) if (size < elementData.length)
{ {
@ -62,7 +64,7 @@ public class FastList<E>
// overflow-conscious code // overflow-conscious code
int oldCapacity = elementData.length; int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); int newCapacity = oldCapacity + (oldCapacity >> 1);
Object[] newElementData = new Object[newCapacity]; Statement[] newElementData = new Statement[newCapacity];
System.arraycopy(element, 0, newElementData, 0, oldCapacity); System.arraycopy(element, 0, newElementData, 0, oldCapacity);
newElementData[size++] = element; newElementData[size++] = element;
elementData = newElementData; elementData = newElementData;
@ -75,15 +77,15 @@ public class FastList<E>
* @param index the index of the element to get * @param index the index of the element to get
* @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid * @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid
*/ */
@SuppressWarnings("unchecked") public Statement get(int index)
public E get(int index)
{ {
return (E) elementData[index]; return elementData[index];
} }
/** /**
* This remove method is most efficient when the element being removed * This remove method is most efficient when the element being removed
* is the last element. Equality is identity based, not equals() based. * is the last element. Equality is identity based, not equals() based.
* Only the first matching element is removed.
* *
* @param element the element to remove * @param element the element to remove
*/ */
Loading…
Cancel
Save