Skip to content

Commit a905eaa

Browse files
author
Dave Cramer
committed
Aaron's patch for Pooled Connections
1 parent 0046d80 commit a905eaa

File tree

2 files changed

+168
-6
lines changed

2 files changed

+168
-6
lines changed

src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package org.postgresql.jdbc2.optional;
22

33
import javax.sql.*;
4-
import java.sql.SQLException;
5-
import java.sql.Connection;
4+
import java.sql.*;
65
import java.util.*;
76
import java.lang.reflect.*;
87

@@ -13,7 +12,7 @@
1312
* @see ConnectionPool
1413
*
1514
* @author Aaron Mulder (ammulder@chariotsolutions.com)
16-
* @version $Revision: 1.3 $
15+
* @version $Revision: 1.4 $
1716
*/
1817
public class PooledConnectionImpl implements PooledConnection
1918
{
@@ -115,7 +114,9 @@ public Connection getConnection() throws SQLException
115114
con.setAutoCommit(autoCommit);
116115
ConnectionHandler handler = new ConnectionHandler(con);
117116
last = 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)
166167
private class ConnectionHandler implements InvocationHandler
167168
{
168169
private Connection con;
170+
private Connection proxy; // the Connection the client is currently using, which is a proxy
169171
private boolean automatic = false;
170172

171173
public ConnectionHandler(Connection con)
@@ -229,6 +231,7 @@ public Object invoke(Object proxy, Method method, Object[] args)
229231
}
230232
con.clearWarnings();
231233
con = null;
234+
proxy = null;
232235
last = null;
233236
fireConnectionClosed();
234237
if (ex != null)
@@ -237,20 +240,123 @@ public Object invoke(Object proxy, Method method, Object[] args)
237240
}
238241
return 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+
}
240258
else
241259
{
242260
return 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+
246272
public void close()
247273
{
248274
if (con != null)
249275
{
250276
automatic = true;
251277
}
252278
con = 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
}

src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* interface to the PooledConnection is through the CPDS.
1212
*
1313
* @author Aaron Mulder (ammulder@chariotsolutions.com)
14-
* @version $Revision: 1.3 $
14+
* @version $Revision: 1.4 $
1515
*/
1616
public class ConnectionPoolTest extends BaseDataSourceTest
1717
{
@@ -341,7 +341,63 @@ public void testIsClosed()
341341
}
342342
}
343343

344-
/**
344+
/**
345+
* Ensures that a statement generated by a proxied connection returns the
346+
* proxied connection from getConnection() [not the physical connection].
347+
*/
348+
public void testStatementConnection() {
349+
try {
350+
PooledConnection pc = getPooledConnection();
351+
Connection con = pc.getConnection();
352+
Statement s = con.createStatement();
353+
Connection conRetrieved = s.getConnection();
354+
355+
assertTrue(con.getClass().equals(conRetrieved.getClass()));
356+
assertTrue(con.equals(conRetrieved));
357+
} catch (SQLException e) {
358+
fail(e.getMessage());
359+
}
360+
}
361+
362+
/**
363+
* Ensures that a prepared statement generated by a proxied connection
364+
* returns the proxied connection from getConnection() [not the physical
365+
* connection].
366+
*/
367+
public void testPreparedStatementConnection() {
368+
try {
369+
PooledConnection pc = getPooledConnection();
370+
Connection con = pc.getConnection();
371+
PreparedStatement s = con.prepareStatement("select 'x'");
372+
Connection conRetrieved = s.getConnection();
373+
374+
assertTrue(con.getClass().equals(conRetrieved.getClass()));
375+
assertTrue(con.equals(conRetrieved));
376+
} catch (SQLException e) {
377+
fail(e.getMessage());
378+
}
379+
}
380+
381+
/**
382+
* Ensures that a callable statement generated by a proxied connection
383+
* returns the proxied connection from getConnection() [not the physical
384+
* connection].
385+
*/
386+
public void testCallableStatementConnection() {
387+
try {
388+
PooledConnection pc = getPooledConnection();
389+
Connection con = pc.getConnection();
390+
CallableStatement s = con.prepareCall("select 'x'");
391+
Connection conRetrieved = s.getConnection();
392+
393+
assertTrue(con.getClass().equals(conRetrieved.getClass()));
394+
assertTrue(con.equals(conRetrieved));
395+
} catch (SQLException e) {
396+
fail(e.getMessage());
397+
}
398+
}
399+
400+
/**
345401
* Helper class to remove a listener during event dispatching.
346402
*/
347403
private class RemoveClose implements ConnectionEventListener

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy