001: /*-------------------------------------------------------------------------
002: *
003: * Copyright (c) 2004-2005, PostgreSQL Global Development Group
004: *
005: * IDENTIFICATION
006: * $PostgreSQL: pgjdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java,v 1.15 2005/01/11 08:25:48 jurka Exp $
007: *
008: *-------------------------------------------------------------------------
009: */
010: package org.postgresql.test.jdbc2.optional;
011:
012: import org.postgresql.jdbc2.optional.ConnectionPool;
013: import org.postgresql.ds.PGConnectionPoolDataSource;
014: import org.postgresql.test.TestUtil;
015: import javax.sql.*;
016: import java.sql.*;
017: import java.io.*;
018:
019: /**
020: * Tests for the ConnectionPoolDataSource and PooledConnection
021: * implementations. They are tested together because the only client
022: * interface to the PooledConnection is through the CPDS.
023: *
024: * @author Aaron Mulder (ammulder@chariotsolutions.com)
025: */
026: public class ConnectionPoolTest extends BaseDataSourceTest {
027: /**
028: * Constructor required by JUnit
029: */
030: public ConnectionPoolTest(String name) {
031: super (name);
032: }
033:
034: /**
035: * Creates and configures a ConnectionPool
036: */
037: protected void initializeDataSource() {
038: if (bds == null) {
039: bds = new ConnectionPool();
040: bds.setServerName(TestUtil.getServer());
041: bds.setPortNumber(TestUtil.getPort());
042: bds.setDatabaseName(TestUtil.getDatabase());
043: bds.setUser(TestUtil.getUser());
044: bds.setPassword(TestUtil.getPassword());
045: }
046: }
047:
048: /**
049: * Though the normal client interface is to grab a Connection, in
050: * order to test the middleware/server interface, we need to deal
051: * with PooledConnections. Some tests use each.
052: */
053: protected PooledConnection getPooledConnection()
054: throws SQLException {
055: initializeDataSource();
056: // we need to recast to PGConnectionPool rather than
057: // jdbc.optional.ConnectionPool because our ObjectFactory
058: // returns only the top level class, not the specific
059: // jdbc2/jdbc3 implementations.
060: return ((PGConnectionPoolDataSource) bds).getPooledConnection();
061: }
062:
063: /**
064: * Instead of just fetching a Connection from the ConnectionPool,
065: * get a PooledConnection, add a listener to close it when the
066: * Connection is closed, and then get the Connection. Without
067: * the listener the PooledConnection (and thus the physical connection)
068: * would never by closed. Probably not a disaster during testing, but
069: * you never know.
070: */
071: protected Connection getDataSourceConnection() throws SQLException {
072: initializeDataSource();
073: final PooledConnection pc = getPooledConnection();
074: // Since the pooled connection won't be reused in these basic tests, close it when the connection is closed
075: pc.addConnectionEventListener(new ConnectionEventListener() {
076: public void connectionClosed(ConnectionEvent event) {
077: try {
078: pc.close();
079: } catch (SQLException e) {
080: fail("Unable to close PooledConnection: " + e);
081: }
082: }
083:
084: public void connectionErrorOccurred(ConnectionEvent event) {
085: }
086: });
087: return pc.getConnection();
088: }
089:
090: /**
091: * Makes sure that if you get a connection from a PooledConnection,
092: * close it, and then get another one, you're really using the same
093: * physical connection. Depends on the implementation of toString
094: * for the connection handle.
095: */
096: public void testPoolReuse() {
097: try {
098: PooledConnection pc = getPooledConnection();
099: con = pc.getConnection();
100: String name = con.toString();
101: con.close();
102: con = pc.getConnection();
103: String name2 = con.toString();
104: con.close();
105: pc.close();
106: assertTrue(
107: "Physical connection doesn't appear to be reused across PooledConnection wrappers",
108: name.equals(name2));
109: } catch (SQLException e) {
110: fail(e.getMessage());
111: }
112: }
113:
114: /**
115: * Makes sure that when you request a connection from the
116: * PooledConnection, and previous connection it might have given
117: * out is closed. See JDBC 2.0 Optional Package spec section
118: * 6.2.3
119: */
120: public void testPoolCloseOldWrapper() {
121: try {
122: PooledConnection pc = getPooledConnection();
123: con = pc.getConnection();
124: Connection con2 = pc.getConnection();
125: try {
126: con.createStatement();
127: fail("Original connection wrapper should be closed when new connection wrapper is generated");
128: } catch (SQLException e) {
129: }
130: con2.close();
131: pc.close();
132: } catch (SQLException e) {
133: fail(e.getMessage());
134: }
135: }
136:
137: /**
138: * Makes sure that if you get two connection wrappers from the same
139: * PooledConnection, they are different, even though the represent
140: * the same physical connection. See JDBC 2.0 Optional Pacakge spec
141: * section 6.2.2
142: */
143: public void testPoolNewWrapper() {
144: try {
145: PooledConnection pc = getPooledConnection();
146: con = pc.getConnection();
147: Connection con2 = pc.getConnection();
148: con2.close();
149: pc.close();
150: assertTrue(
151: "Two calls to PooledConnection.getConnection should not return the same connection wrapper",
152: con != con2);
153: } catch (SQLException e) {
154: fail(e.getMessage());
155: }
156: }
157:
158: /**
159: * Makes sure that exactly one close event is fired for each time a
160: * connection handle is closed. Also checks that events are not
161: * fired after a given handle has been closed once.
162: */
163: public void testCloseEvent() {
164: try {
165: PooledConnection pc = getPooledConnection();
166: CountClose cc = new CountClose();
167: pc.addConnectionEventListener(cc);
168: con = pc.getConnection();
169: assertTrue(cc.getCount() == 0);
170: assertTrue(cc.getErrorCount() == 0);
171: con.close();
172: assertTrue(cc.getCount() == 1);
173: assertTrue(cc.getErrorCount() == 0);
174: con = pc.getConnection();
175: assertTrue(cc.getCount() == 1);
176: assertTrue(cc.getErrorCount() == 0);
177: con.close();
178: assertTrue(cc.getCount() == 2);
179: assertTrue(cc.getErrorCount() == 0);
180: // a double close shouldn't fire additional events
181: con.close();
182: assertTrue(cc.getCount() == 2);
183: assertTrue(cc.getErrorCount() == 0);
184: pc.close();
185: } catch (SQLException e) {
186: fail(e.getMessage());
187: }
188: }
189:
190: /**
191: * Makes sure that close events are not fired after a listener has
192: * been removed.
193: */
194: public void testNoCloseEvent() {
195: try {
196: PooledConnection pc = getPooledConnection();
197: CountClose cc = new CountClose();
198: pc.addConnectionEventListener(cc);
199: con = pc.getConnection();
200: assertTrue(cc.getCount() == 0);
201: assertTrue(cc.getErrorCount() == 0);
202: con.close();
203: assertTrue(cc.getCount() == 1);
204: assertTrue(cc.getErrorCount() == 0);
205: pc.removeConnectionEventListener(cc);
206: con = pc.getConnection();
207: assertTrue(cc.getCount() == 1);
208: assertTrue(cc.getErrorCount() == 0);
209: con.close();
210: assertTrue(cc.getCount() == 1);
211: assertTrue(cc.getErrorCount() == 0);
212: } catch (SQLException e) {
213: fail(e.getMessage());
214: }
215: }
216:
217: /**
218: * Makes sure that a listener can be removed while dispatching
219: * events. Sometimes this causes a ConcurrentModificationException
220: * or something.
221: */
222: public void testInlineCloseEvent() {
223: try {
224: PooledConnection pc = getPooledConnection();
225: RemoveClose rc1 = new RemoveClose();
226: RemoveClose rc2 = new RemoveClose();
227: RemoveClose rc3 = new RemoveClose();
228: pc.addConnectionEventListener(rc1);
229: pc.addConnectionEventListener(rc2);
230: pc.addConnectionEventListener(rc3);
231: con = pc.getConnection();
232: con.close();
233: con = pc.getConnection();
234: con.close();
235: } catch (Exception e) {
236: fail(e.getMessage());
237: }
238: }
239:
240: /**
241: * Tests that a close event is not generated when a connection
242: * handle is closed automatically due to a new connection handle
243: * being opened for the same PooledConnection. See JDBC 2.0
244: * Optional Package spec section 6.3
245: */
246: public void testAutomaticCloseEvent() {
247: try {
248: PooledConnection pc = getPooledConnection();
249: CountClose cc = new CountClose();
250: pc.addConnectionEventListener(cc);
251: con = pc.getConnection();
252: assertTrue(cc.getCount() == 0);
253: assertTrue(cc.getErrorCount() == 0);
254: con.close();
255: assertTrue(cc.getCount() == 1);
256: assertTrue(cc.getErrorCount() == 0);
257: con = pc.getConnection();
258: assertTrue(cc.getCount() == 1);
259: assertTrue(cc.getErrorCount() == 0);
260: // Open a 2nd connection, causing the first to be closed. No even should be generated.
261: Connection con2 = pc.getConnection();
262: assertTrue(
263: "Connection handle was not closed when new handle was opened",
264: con.isClosed());
265: assertTrue(cc.getCount() == 1);
266: assertTrue(cc.getErrorCount() == 0);
267: con2.close();
268: assertTrue(cc.getCount() == 2);
269: assertTrue(cc.getErrorCount() == 0);
270: pc.close();
271: } catch (SQLException e) {
272: fail(e.getMessage());
273: }
274: }
275:
276: /**
277: * Makes sure the isClosed method on a connection wrapper does what
278: * you'd expect. Checks the usual case, as well as automatic
279: * closure when a new handle is opened on the same physical connection.
280: */
281: public void testIsClosed() {
282: try {
283: PooledConnection pc = getPooledConnection();
284: Connection con = pc.getConnection();
285: assertTrue(!con.isClosed());
286: con.close();
287: assertTrue(con.isClosed());
288: con = pc.getConnection();
289: Connection con2 = pc.getConnection();
290: assertTrue(con.isClosed());
291: assertTrue(!con2.isClosed());
292: con2.close();
293: assertTrue(con.isClosed());
294: pc.close();
295: } catch (SQLException e) {
296: fail(e.getMessage());
297: }
298: }
299:
300: /**
301: * Ensures that a statement generated by a proxied connection returns the
302: * proxied connection from getConnection() [not the physical connection].
303: */
304: public void testStatementConnection() {
305: try {
306: PooledConnection pc = getPooledConnection();
307: Connection con = pc.getConnection();
308: Statement s = con.createStatement();
309: Connection conRetrieved = s.getConnection();
310:
311: assertTrue(con.getClass().equals(conRetrieved.getClass()));
312: assertTrue(con.equals(conRetrieved));
313: } catch (SQLException e) {
314: fail(e.getMessage());
315: }
316: }
317:
318: /**
319: * Ensures that the Statement proxy generated by the Connection handle
320: * throws the correct kind of exception.
321: */
322: public void testStatementProxy() {
323: Statement s = null;
324: try {
325: PooledConnection pc = getPooledConnection();
326: Connection con = pc.getConnection();
327: s = con.createStatement();
328: } catch (SQLException e) {
329: fail(e.getMessage());
330: }
331: try {
332: s.executeQuery("SELECT * FROM THIS_TABLE_SHOULD_NOT_EXIST");
333: fail("An SQL exception was not thrown that should have been");
334: } catch (SQLException e) {
335: ; // This is the expected and correct path
336: } catch (Exception e) {
337: fail("bad exception; was expecting SQLException, not"
338: + e.getClass().getName());
339: }
340: }
341:
342: /**
343: * Ensures that a prepared statement generated by a proxied connection
344: * returns the proxied connection from getConnection() [not the physical
345: * connection].
346: */
347: public void testPreparedStatementConnection() {
348: try {
349: PooledConnection pc = getPooledConnection();
350: Connection con = pc.getConnection();
351: PreparedStatement s = con.prepareStatement("select 'x'");
352: Connection conRetrieved = s.getConnection();
353:
354: assertTrue(con.getClass().equals(conRetrieved.getClass()));
355: assertTrue(con.equals(conRetrieved));
356: } catch (SQLException e) {
357: fail(e.getMessage());
358: }
359: }
360:
361: /**
362: * Ensures that a callable statement generated by a proxied connection
363: * returns the proxied connection from getConnection() [not the physical
364: * connection].
365: */
366: public void testCallableStatementConnection() {
367: try {
368: PooledConnection pc = getPooledConnection();
369: Connection con = pc.getConnection();
370: CallableStatement s = con.prepareCall("select 'x'");
371: Connection conRetrieved = s.getConnection();
372:
373: assertTrue(con.getClass().equals(conRetrieved.getClass()));
374: assertTrue(con.equals(conRetrieved));
375: } catch (SQLException e) {
376: fail(e.getMessage());
377: }
378: }
379:
380: /**
381: * Ensure that a statement created from a pool can be used
382: * like any other statement in regard to pg extensions.
383: */
384: public void testStatementsProxyPGStatement() {
385: try {
386: PooledConnection pc = getPooledConnection();
387: con = pc.getConnection();
388:
389: Statement s = con.createStatement();
390: boolean b = ((org.postgresql.PGStatement) s)
391: .isUseServerPrepare();
392:
393: PreparedStatement ps = con.prepareStatement("select 'x'");
394: b = ((org.postgresql.PGStatement) ps).isUseServerPrepare();
395:
396: CallableStatement cs = con.prepareCall("select 'x'");
397: b = ((org.postgresql.PGStatement) cs).isUseServerPrepare();
398:
399: } catch (SQLException e) {
400: fail(e.getMessage());
401: }
402: }
403:
404: /**
405: * Helper class to remove a listener during event dispatching.
406: */
407: private class RemoveClose implements ConnectionEventListener {
408: public void connectionClosed(ConnectionEvent event) {
409: ((PooledConnection) event.getSource())
410: .removeConnectionEventListener(this );
411: }
412:
413: public void connectionErrorOccurred(ConnectionEvent event) {
414: ((PooledConnection) event.getSource())
415: .removeConnectionEventListener(this );
416: }
417: }
418:
419: /**
420: * Helper class that implements the event listener interface, and
421: * counts the number of events it sees.
422: */
423: private class CountClose implements ConnectionEventListener {
424: private int count = 0, errorCount = 0;
425:
426: public void connectionClosed(ConnectionEvent event) {
427: count++;
428: }
429:
430: public void connectionErrorOccurred(ConnectionEvent event) {
431: errorCount++;
432: }
433:
434: public int getCount() {
435: return count;
436: }
437:
438: public int getErrorCount() {
439: return errorCount;
440: }
441:
442: public void clear() {
443: count = errorCount = 0;
444: }
445: }
446:
447: public void testSerializable() throws IOException,
448: ClassNotFoundException {
449: ConnectionPool pool = new ConnectionPool();
450: pool.setDefaultAutoCommit(false);
451: pool.setServerName("db.myhost.com");
452: pool.setDatabaseName("mydb");
453: pool.setUser("user");
454: pool.setPassword("pass");
455: pool.setPortNumber(1111);
456:
457: ByteArrayOutputStream baos = new ByteArrayOutputStream();
458: ObjectOutputStream oos = new ObjectOutputStream(baos);
459: oos.writeObject(pool);
460:
461: ByteArrayInputStream bais = new ByteArrayInputStream(baos
462: .toByteArray());
463: ObjectInputStream ois = new ObjectInputStream(bais);
464: ConnectionPool pool2 = (ConnectionPool) ois.readObject();
465:
466: assertEquals(pool.isDefaultAutoCommit(), pool2
467: .isDefaultAutoCommit());
468: assertEquals(pool.getServerName(), pool2.getServerName());
469: assertEquals(pool.getDatabaseName(), pool2.getDatabaseName());
470: assertEquals(pool.getUser(), pool2.getUser());
471: assertEquals(pool.getPassword(), pool2.getPassword());
472: assertEquals(pool.getPortNumber(), pool2.getPortNumber());
473: }
474:
475: }
|