11package org .postgresql .jdbc2 .optional ;
22
33import javax .sql .*;
4- import java .sql .SQLException ;
5- import java .sql .Connection ;
4+ import java .sql .*;
65import java .util .*;
76import java .lang .reflect .*;
87
1312 * @see ConnectionPool
1413 *
1514 * @author Aaron Mulder (ammulder@chariotsolutions.com)
16- * @version $Revision: 1.3 $
15+ * @version $Revision: 1.4 $
1716 */
1817public class PooledConnectionImpl implements PooledConnection
1918{
@@ -115,7 +114,9 @@ public Connection getConnection() throws SQLException
115114con .setAutoCommit (autoCommit );
116115ConnectionHandler handler =new ConnectionHandler (con );
117116last =handler ;
118- return (Connection )Proxy .newProxyInstance (getClass ().getClassLoader (),new Class []{Connection .class },handler );
117+ Connection con = (Connection )Proxy .newProxyInstance (getClass ().getClassLoader (),new Class []{Connection .class },handler );
118+ last .setProxy (con );
119+ return con ;
119120}
120121
121122/**
@@ -166,6 +167,7 @@ void fireConnectionFatalError(SQLException e)
166167private class ConnectionHandler implements InvocationHandler
167168{
168169private Connection con ;
170+ private Connection proxy ;// the Connection the client is currently using, which is a proxy
169171private boolean automatic =false ;
170172
171173public ConnectionHandler (Connection con )
@@ -229,6 +231,7 @@ public Object invoke(Object proxy, Method method, Object[] args)
229231}
230232con .clearWarnings ();
231233con =null ;
234+ proxy =null ;
232235last =null ;
233236fireConnectionClosed ();
234237if (ex !=null )
@@ -237,20 +240,123 @@ public Object invoke(Object proxy, Method method, Object[] args)
237240}
238241return null ;
239242}
243+ else if (method .getName ().equals ("createStatement" ))
244+ {
245+ Statement st = (Statement )method .invoke (con ,args );
246+ return Proxy .newProxyInstance (getClass ().getClassLoader (),new Class []{Statement .class },new StatementHandler (this ,st ));
247+ }
248+ else if (method .getName ().equals ("prepareCall" ))
249+ {
250+ Statement st = (Statement )method .invoke (con ,args );
251+ return Proxy .newProxyInstance (getClass ().getClassLoader (),new Class []{CallableStatement .class },new StatementHandler (this ,st ));
252+ }
253+ else if (method .getName ().equals ("prepareStatement" ))
254+ {
255+ Statement st = (Statement )method .invoke (con ,args );
256+ return Proxy .newProxyInstance (getClass ().getClassLoader (),new Class []{PreparedStatement .class },new StatementHandler (this ,st ));
257+ }
240258else
241259{
242260return method .invoke (con ,args );
243261}
244262}
245263
264+ Connection getProxy () {
265+ return proxy ;
266+ }
267+
268+ void setProxy (Connection proxy ) {
269+ this .proxy =proxy ;
270+ }
271+
246272public void close ()
247273{
248274if (con !=null )
249275{
250276automatic =true ;
251277}
252278con =null ;
279+ proxy =null ;
253280// No close event fired here: see JDBC 2.0 Optional Package spec section 6.3
254281}
282+
283+ public boolean isClosed () {
284+ return con ==null ;
285+ }
255286}
287+
288+ /**
289+ * Instead of declaring classes implementing Statement, PreparedStatement,
290+ * and CallableStatement, which would have to be updated for every JDK rev,
291+ * use a dynamic proxy to handle all calls through the Statement
292+ * interfaces.This is the part that requires JDK 1.3 or higher, though
293+ * JDK 1.2 could be supported with a 3rd-party proxy package.
294+ *
295+ * The StatementHandler is required in order to return the proper
296+ * Connection proxy for the getConnection method.
297+ */
298+ private static class StatementHandler implements InvocationHandler {
299+ private ConnectionHandler con ;
300+ private Statement st ;
301+
302+ public StatementHandler (ConnectionHandler con ,Statement st ) {
303+ this .con =con ;
304+ this .st =st ;
305+ }
306+ public Object invoke (Object proxy ,Method method ,Object []args )
307+ throws Throwable
308+ {
309+ // From Object
310+ if (method .getDeclaringClass ().getName ().equals ("java.lang.Object" ))
311+ {
312+ if (method .getName ().equals ("toString" ))
313+ {
314+ return "Pooled statement wrapping physical statement " +st ;
315+ }
316+ if (method .getName ().equals ("hashCode" ))
317+ {
318+ return new Integer (st .hashCode ());
319+ }
320+ if (method .getName ().equals ("equals" ))
321+ {
322+ if (args [0 ] ==null )
323+ {
324+ return Boolean .FALSE ;
325+ }
326+ try
327+ {
328+ return Proxy .isProxyClass (args [0 ].getClass ()) && ((StatementHandler )Proxy .getInvocationHandler (args [0 ])).st ==st ?Boolean .TRUE :Boolean .FALSE ;
329+ }
330+ catch (ClassCastException e )
331+ {
332+ return Boolean .FALSE ;
333+ }
334+ }
335+ return method .invoke (st ,args );
336+ }
337+ // All the rest is from the Statement interface
338+ if (st ==null ||con .isClosed ())
339+ {
340+ throw new SQLException ("Statement has been closed" );
341+ }
342+ if (method .getName ().equals ("close" ))
343+ {
344+ try {
345+ st .close ();
346+ }finally {
347+ con =null ;
348+ st =null ;
349+ return null ;
350+ }
351+ }
352+ else if (method .getName ().equals ("getConnection" ))
353+ {
354+ return con .getProxy ();// the proxied connection, not a physical connection
355+ }
356+ else
357+ {
358+ return method .invoke (st ,args );
359+ }
360+ }
361+ }
256362}