From a905eaacf015bd9903432faeb8f1f0a0f0a0c0cf Mon Sep 17 00:00:00 2001 From: Dave Cramer <davec@fastcrypt.com> Date: Wed, 11 Dec 2002 11:42:14 +0000 Subject: [PATCH] Aaron's patch for Pooled Connections --- .../jdbc2/optional/PooledConnectionImpl.java | 114 +++++++++++++++++- .../jdbc2/optional/ConnectionPoolTest.java | 60 ++++++++- 2 files changed, 168 insertions(+), 6 deletions(-) diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java index 1d1d8f67df2..f6717f71b61 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java @@ -1,8 +1,7 @@ package org.postgresql.jdbc2.optional; import javax.sql.*; -import java.sql.SQLException; -import java.sql.Connection; +import java.sql.*; import java.util.*; import java.lang.reflect.*; @@ -13,7 +12,7 @@ import java.lang.reflect.*; * @see ConnectionPool * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.3 $ + * @version $Revision: 1.4 $ */ public class PooledConnectionImpl implements PooledConnection { @@ -115,7 +114,9 @@ public class PooledConnectionImpl implements PooledConnection con.setAutoCommit(autoCommit); ConnectionHandler handler = new ConnectionHandler(con); last = handler; - return (Connection)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, handler); + Connection con = (Connection)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, handler); + last.setProxy(con); + return con; } /** @@ -166,6 +167,7 @@ public class PooledConnectionImpl implements PooledConnection private class ConnectionHandler implements InvocationHandler { private Connection con; + private Connection proxy; // the Connection the client is currently using, which is a proxy private boolean automatic = false; public ConnectionHandler(Connection con) @@ -229,6 +231,7 @@ public class PooledConnectionImpl implements PooledConnection } con.clearWarnings(); con = null; + proxy = null; last = null; fireConnectionClosed(); if (ex != null) @@ -237,12 +240,35 @@ public class PooledConnectionImpl implements PooledConnection } return null; } + else if(method.getName().equals("createStatement")) + { + Statement st = (Statement)method.invoke(con, args); + return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Statement.class}, new StatementHandler(this, st)); + } + else if(method.getName().equals("prepareCall")) + { + Statement st = (Statement)method.invoke(con, args); + return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{CallableStatement.class}, new StatementHandler(this, st)); + } + else if(method.getName().equals("prepareStatement")) + { + Statement st = (Statement)method.invoke(con, args); + return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{PreparedStatement.class}, new StatementHandler(this, st)); + } else { return method.invoke(con, args); } } + Connection getProxy() { + return proxy; + } + + void setProxy(Connection proxy) { + this.proxy = proxy; + } + public void close() { if (con != null) @@ -250,7 +276,87 @@ public class PooledConnectionImpl implements PooledConnection automatic = true; } con = null; + proxy = null; // No close event fired here: see JDBC 2.0 Optional Package spec section 6.3 } + + public boolean isClosed() { + return con == null; + } } + + /** + * Instead of declaring classes implementing Statement, PreparedStatement, + * and CallableStatement, which would have to be updated for every JDK rev, + * use a dynamic proxy to handle all calls through the Statement + * interfaces. This is the part that requires JDK 1.3 or higher, though + * JDK 1.2 could be supported with a 3rd-party proxy package. + * + * The StatementHandler is required in order to return the proper + * Connection proxy for the getConnection method. + */ + private static class StatementHandler implements InvocationHandler { + private ConnectionHandler con; + private Statement st; + + public StatementHandler(ConnectionHandler con, Statement st) { + this.con = con; + this.st = st; + } + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + // From Object + if (method.getDeclaringClass().getName().equals("java.lang.Object")) + { + if (method.getName().equals("toString")) + { + return "Pooled statement wrapping physical statement " + st; + } + if (method.getName().equals("hashCode")) + { + return new Integer(st.hashCode()); + } + if (method.getName().equals("equals")) + { + if (args[0] == null) + { + return Boolean.FALSE; + } + try + { + return Proxy.isProxyClass(args[0].getClass()) && ((StatementHandler) Proxy.getInvocationHandler(args[0])).st == st ? Boolean.TRUE : Boolean.FALSE; + } + catch (ClassCastException e) + { + return Boolean.FALSE; + } + } + return method.invoke(st, args); + } + // All the rest is from the Statement interface + if (st == null || con.isClosed()) + { + throw new SQLException("Statement has been closed"); + } + if (method.getName().equals("close")) + { + try { + st.close(); + } finally { + con = null; + st = null; + return null; + } + } + else if (method.getName().equals("getConnection")) + { + return con.getProxy(); // the proxied connection, not a physical connection + } + else + { + return method.invoke(st, args); + } + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java index 90a3b564a51..06f455ea3c7 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java @@ -11,7 +11,7 @@ import java.sql.*; * interface to the PooledConnection is through the CPDS. * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.3 $ + * @version $Revision: 1.4 $ */ public class ConnectionPoolTest extends BaseDataSourceTest { @@ -341,7 +341,63 @@ public class ConnectionPoolTest extends BaseDataSourceTest } } - /** + /** + * Ensures that a statement generated by a proxied connection returns the + * proxied connection from getConnection() [not the physical connection]. + */ + public void testStatementConnection() { + try { + PooledConnection pc = getPooledConnection(); + Connection con = pc.getConnection(); + Statement s = con.createStatement(); + Connection conRetrieved = s.getConnection(); + + assertTrue(con.getClass().equals(conRetrieved.getClass())); + assertTrue(con.equals(conRetrieved)); + } catch (SQLException e) { + fail(e.getMessage()); + } + } + + /** + * Ensures that a prepared statement generated by a proxied connection + * returns the proxied connection from getConnection() [not the physical + * connection]. + */ + public void testPreparedStatementConnection() { + try { + PooledConnection pc = getPooledConnection(); + Connection con = pc.getConnection(); + PreparedStatement s = con.prepareStatement("select 'x'"); + Connection conRetrieved = s.getConnection(); + + assertTrue(con.getClass().equals(conRetrieved.getClass())); + assertTrue(con.equals(conRetrieved)); + } catch (SQLException e) { + fail(e.getMessage()); + } + } + + /** + * Ensures that a callable statement generated by a proxied connection + * returns the proxied connection from getConnection() [not the physical + * connection]. + */ + public void testCallableStatementConnection() { + try { + PooledConnection pc = getPooledConnection(); + Connection con = pc.getConnection(); + CallableStatement s = con.prepareCall("select 'x'"); + Connection conRetrieved = s.getConnection(); + + assertTrue(con.getClass().equals(conRetrieved.getClass())); + assertTrue(con.equals(conRetrieved)); + } catch (SQLException e) { + fail(e.getMessage()); + } + } + + /** * Helper class to remove a listener during event dispatching. */ private class RemoveClose implements ConnectionEventListener -- GitLab