Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)