mirror of
https://github.com/postgres/postgres.git
synced 2025-08-28 18:48:04 +03:00
Added DataSource code and tests submitted by Aaron Mulder
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package org.postgresql.test.jdbc2.optional;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.postgresql.test.JDBC2Tests;
|
||||
import org.postgresql.jdbc2.optional.SimpleDataSource;
|
||||
import org.postgresql.jdbc2.optional.BaseDataSource;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* Common tests for all the BaseDataSource implementations. This is
|
||||
* a small variety to make sure that a connection can be opened and
|
||||
* some basic queries run. The different BaseDataSource subclasses
|
||||
* have different subclasses of this which add additional custom
|
||||
* tests.
|
||||
*
|
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
||||
* @version $Revision: 1.1 $
|
||||
*/
|
||||
public abstract class BaseDataSourceTest extends TestCase {
|
||||
protected Connection con;
|
||||
protected BaseDataSource bds;
|
||||
|
||||
/**
|
||||
* Constructor required by JUnit
|
||||
*/
|
||||
public BaseDataSourceTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test table using a standard connection (not from a
|
||||
* DataSource).
|
||||
*/
|
||||
protected void setUp() throws Exception {
|
||||
con = JDBC2Tests.openDB();
|
||||
JDBC2Tests.createTable(con, "poolingtest", "id int4 not null primary key, name varchar(50)");
|
||||
Statement stmt = con.createStatement();
|
||||
stmt.executeUpdate("INSERT INTO poolingtest VALUES (1, 'Test Row 1')");
|
||||
stmt.executeUpdate("INSERT INTO poolingtest VALUES (2, 'Test Row 2')");
|
||||
JDBC2Tests.closeDB(con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the test table using a standard connection (not from
|
||||
* a DataSource)
|
||||
*/
|
||||
protected void tearDown() throws Exception {
|
||||
con = JDBC2Tests.openDB();
|
||||
JDBC2Tests.dropTable(con, "poolingtest");
|
||||
JDBC2Tests.closeDB(con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a connection from the current BaseDataSource
|
||||
*/
|
||||
protected Connection getDataSourceConnection() throws SQLException {
|
||||
initializeDataSource();
|
||||
return bds.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the current BaseDataSource for
|
||||
* testing. Must be customized by each subclass.
|
||||
*/
|
||||
protected abstract void initializeDataSource();
|
||||
|
||||
/**
|
||||
* Test to make sure you can instantiate and configure the
|
||||
* appropriate DataSource
|
||||
*/
|
||||
public void testCreateDataSource() {
|
||||
initializeDataSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to make sure you can get a connection from the DataSource,
|
||||
* which in turn means the DataSource was able to open it.
|
||||
*/
|
||||
public void testGetConnection() {
|
||||
try {
|
||||
con = getDataSourceConnection();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple test to make sure you can execute SQL using the
|
||||
* Connection from the DataSource
|
||||
*/
|
||||
public void testUseConnection() {
|
||||
try {
|
||||
con = getDataSourceConnection();
|
||||
Statement st = con.createStatement();
|
||||
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM poolingtest");
|
||||
if(rs.next()) {
|
||||
int count = rs.getInt(1);
|
||||
if(rs.next()) {
|
||||
fail("Should only have one row in SELECT COUNT result set");
|
||||
}
|
||||
if(count != 2) {
|
||||
fail("Count returned "+count+" expecting 2");
|
||||
}
|
||||
} else {
|
||||
fail("Should have one row in SELECT COUNT result set");
|
||||
}
|
||||
rs.close();
|
||||
st.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test to make sure you can execute DDL SQL using the
|
||||
* Connection from the DataSource.
|
||||
*/
|
||||
public void testDdlOverConnection() {
|
||||
try {
|
||||
con = getDataSourceConnection();
|
||||
JDBC2Tests.dropTable(con, "poolingtest");
|
||||
JDBC2Tests.createTable(con, "poolingtest", "id int4 not null primary key, name varchar(50)");
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test to make sure the connections are not being pooled by the
|
||||
* current DataSource. Obviously need to be overridden in the case
|
||||
* of a pooling Datasource.
|
||||
*/
|
||||
public void testNotPooledConnection() {
|
||||
try {
|
||||
con = getDataSourceConnection();
|
||||
String name = con.toString();
|
||||
con.close();
|
||||
con = getDataSourceConnection();
|
||||
String name2 = con.toString();
|
||||
con.close();
|
||||
assertTrue(!name.equals(name2));
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventually, we must test stuffing the DataSource in JNDI and
|
||||
* then getting it back out and make sure it's still usable. This
|
||||
* should ideally test both Serializable and Referenceable
|
||||
* mechanisms. Will probably be multiple tests when implemented.
|
||||
*/
|
||||
public void testJndi() {
|
||||
// TODO: Put the DS in JNDI, retrieve it, and try some of this stuff again
|
||||
}
|
||||
}
|
@@ -0,0 +1,327 @@
|
||||
package org.postgresql.test.jdbc2.optional;
|
||||
|
||||
import org.postgresql.jdbc2.optional.ConnectionPool;
|
||||
import org.postgresql.test.JDBC2Tests;
|
||||
import javax.sql.*;
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* Tests for the ConnectionPoolDataSource and PooledConnection
|
||||
* implementations. They are tested together because the only client
|
||||
* interface to the PooledConnection is through the CPDS.
|
||||
*
|
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
||||
* @version $Revision: 1.1 $
|
||||
*/
|
||||
public class ConnectionPoolTest extends BaseDataSourceTest {
|
||||
/**
|
||||
* Constructor required by JUnit
|
||||
*/
|
||||
public ConnectionPoolTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures a ConnectionPool
|
||||
*/
|
||||
protected void initializeDataSource() {
|
||||
if(bds == null) {
|
||||
bds = new ConnectionPool();
|
||||
String db = JDBC2Tests.getURL();
|
||||
if(db.indexOf('/') > -1) {
|
||||
db = db.substring(db.lastIndexOf('/')+1);
|
||||
} else if(db.indexOf(':') > -1) {
|
||||
db = db.substring(db.lastIndexOf(':')+1);
|
||||
}
|
||||
bds.setDatabaseName(db);
|
||||
bds.setUser(JDBC2Tests.getUser());
|
||||
bds.setPassword(JDBC2Tests.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Though the normal client interface is to grab a Connection, in
|
||||
* order to test the middleware/server interface, we need to deal
|
||||
* with PooledConnections. Some tests use each.
|
||||
*/
|
||||
protected PooledConnection getPooledConnection() throws SQLException {
|
||||
initializeDataSource();
|
||||
return ((ConnectionPool)bds).getPooledConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of just fetching a Connection from the ConnectionPool,
|
||||
* get a PooledConnection, add a listener to close it when the
|
||||
* Connection is closed, and then get the Connection. Without
|
||||
* the listener the PooledConnection (and thus the physical connection)
|
||||
* would never by closed. Probably not a disaster during testing, but
|
||||
* you never know.
|
||||
*/
|
||||
protected Connection getDataSourceConnection() throws SQLException {
|
||||
initializeDataSource();
|
||||
final PooledConnection pc = getPooledConnection();
|
||||
// Since the pooled connection won't be reused in these basic tests, close it when the connection is closed
|
||||
pc.addConnectionEventListener(new ConnectionEventListener() {
|
||||
public void connectionClosed(ConnectionEvent event) {
|
||||
try {
|
||||
pc.close();
|
||||
} catch (SQLException e) {
|
||||
fail("Unable to close PooledConnection: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
public void connectionErrorOccurred(ConnectionEvent event) {
|
||||
}
|
||||
});
|
||||
return pc.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that if you get a connection from a PooledConnection,
|
||||
* close it, and then get another one, you're really using the same
|
||||
* physical connection. Depends on the implementation of toString
|
||||
* for the connection handle.
|
||||
*/
|
||||
public void testPoolReuse() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
con = pc.getConnection();
|
||||
String name = con.toString();
|
||||
con.close();
|
||||
con = pc.getConnection();
|
||||
String name2 = con.toString();
|
||||
con.close();
|
||||
pc.close();
|
||||
assertTrue("Physical connection doesn't appear to be reused across PooledConnection wrappers", name.equals(name2));
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that when you request a connection from the
|
||||
* PooledConnection, and previous connection it might have given
|
||||
* out is closed. See JDBC 2.0 Optional Package spec section
|
||||
* 6.2.3
|
||||
*/
|
||||
public void testPoolCloseOldWrapper() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
con = pc.getConnection();
|
||||
Connection con2 = pc.getConnection();
|
||||
try {
|
||||
con.createStatement();
|
||||
fail("Original connection wrapper should be closed when new connection wrapper is generated");
|
||||
} catch(SQLException e) {}
|
||||
try {
|
||||
con.close();
|
||||
fail("Original connection wrapper should be closed when new connection wrapper is generated");
|
||||
} catch(SQLException e) {}
|
||||
con2.close();
|
||||
pc.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that if you get two connection wrappers from the same
|
||||
* PooledConnection, they are different, even though the represent
|
||||
* the same physical connection. See JDBC 2.0 Optional Pacakge spec
|
||||
* section 6.2.2
|
||||
*/
|
||||
public void testPoolNewWrapper() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
con = pc.getConnection();
|
||||
Connection con2 = pc.getConnection();
|
||||
con2.close();
|
||||
pc.close();
|
||||
assertTrue("Two calls to PooledConnection.getConnection should not return the same connection wrapper", con != con2);
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that exactly one close event is fired for each time a
|
||||
* connection handle is closed. Also checks that events are not
|
||||
* fired after a given handle has been closed once.
|
||||
*/
|
||||
public void testCloseEvent() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
CountClose cc = new CountClose();
|
||||
pc.addConnectionEventListener(cc);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 0);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con.close();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con.close();
|
||||
assertTrue(cc.getCount() == 2);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
try {
|
||||
con.close();
|
||||
fail("Should not be able to close a connection wrapper twice");
|
||||
} catch (SQLException e) {}
|
||||
assertTrue(cc.getCount() == 2);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
pc.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that close events are not fired after a listener has
|
||||
* been removed.
|
||||
*/
|
||||
public void testNoCloseEvent() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
CountClose cc = new CountClose();
|
||||
pc.addConnectionEventListener(cc);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 0);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con.close();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
pc.removeConnectionEventListener(cc);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con.close();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that a listener can be removed while dispatching
|
||||
* events. Sometimes this causes a ConcurrentModificationException
|
||||
* or something.
|
||||
*/
|
||||
public void testInlineCloseEvent() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
RemoveClose rc1 = new RemoveClose();
|
||||
RemoveClose rc2 = new RemoveClose();
|
||||
RemoveClose rc3 = new RemoveClose();
|
||||
pc.addConnectionEventListener(rc1);
|
||||
pc.addConnectionEventListener(rc2);
|
||||
pc.addConnectionEventListener(rc3);
|
||||
con = pc.getConnection();
|
||||
con.close();
|
||||
con = pc.getConnection();
|
||||
con.close();
|
||||
} catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a close event is not generated when a connection
|
||||
* handle is closed automatically due to a new connection handle
|
||||
* being opened for the same PooledConnection. See JDBC 2.0
|
||||
* Optional Package spec section 6.3
|
||||
*/
|
||||
public void testAutomaticCloseEvent() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
CountClose cc = new CountClose();
|
||||
pc.addConnectionEventListener(cc);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 0);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con.close();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con = pc.getConnection();
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
// Open a 2nd connection, causing the first to be closed. No even should be generated.
|
||||
Connection con2 = pc.getConnection();
|
||||
assertTrue("Connection handle was not closed when new handle was opened", con.isClosed());
|
||||
assertTrue(cc.getCount() == 1);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
con2.close();
|
||||
assertTrue(cc.getCount() == 2);
|
||||
assertTrue(cc.getErrorCount() == 0);
|
||||
pc.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the isClosed method on a connection wrapper does what
|
||||
* you'd expect. Checks the usual case, as well as automatic
|
||||
* closure when a new handle is opened on the same physical connection.
|
||||
*/
|
||||
public void testIsClosed() {
|
||||
try {
|
||||
PooledConnection pc = getPooledConnection();
|
||||
Connection con = pc.getConnection();
|
||||
assertTrue(!con.isClosed());
|
||||
con.close();
|
||||
assertTrue(con.isClosed());
|
||||
con = pc.getConnection();
|
||||
Connection con2 = pc.getConnection();
|
||||
assertTrue(con.isClosed());
|
||||
assertTrue(!con2.isClosed());
|
||||
con2.close();
|
||||
assertTrue(con.isClosed());
|
||||
pc.close();
|
||||
} catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to remove a listener during event dispatching.
|
||||
*/
|
||||
private class RemoveClose implements ConnectionEventListener {
|
||||
public void connectionClosed(ConnectionEvent event) {
|
||||
((PooledConnection)event.getSource()).removeConnectionEventListener(this);
|
||||
}
|
||||
|
||||
public void connectionErrorOccurred(ConnectionEvent event) {
|
||||
((PooledConnection)event.getSource()).removeConnectionEventListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that implements the event listener interface, and
|
||||
* counts the number of events it sees.
|
||||
*/
|
||||
private class CountClose implements ConnectionEventListener {
|
||||
private int count = 0, errorCount = 0;
|
||||
public void connectionClosed(ConnectionEvent event) {
|
||||
count++;
|
||||
}
|
||||
|
||||
public void connectionErrorOccurred(ConnectionEvent event) {
|
||||
errorCount++;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
count = errorCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package org.postgresql.test.jdbc2.optional;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Test suite for the JDBC 2.0 Optional Package implementation. This
|
||||
* includes the DataSource, ConnectionPoolDataSource, and
|
||||
* PooledConnection implementations.
|
||||
*
|
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
||||
* @version $Revision: 1.1 $
|
||||
*/
|
||||
public class OptionalTestSuite extends TestSuite {
|
||||
/**
|
||||
* Gets the test suite for the entire JDBC 2.0 Optional Package
|
||||
* implementation.
|
||||
*/
|
||||
public static TestSuite suite() {
|
||||
TestSuite suite = new TestSuite();
|
||||
suite.addTestSuite(SimpleDataSourceTest.class);
|
||||
suite.addTestSuite(ConnectionPoolTest.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package org.postgresql.test.jdbc2.optional;
|
||||
|
||||
import org.postgresql.test.JDBC2Tests;
|
||||
import org.postgresql.jdbc2.optional.SimpleDataSource;
|
||||
|
||||
/**
|
||||
* Performs the basic tests defined in the superclass. Just adds the
|
||||
* configuration logic.
|
||||
*
|
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com)
|
||||
* @version $Revision: 1.1 $
|
||||
*/
|
||||
public class SimpleDataSourceTest extends BaseDataSourceTest {
|
||||
/**
|
||||
* Constructor required by JUnit
|
||||
*/
|
||||
public SimpleDataSourceTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures a new SimpleDataSource.
|
||||
*/
|
||||
protected void initializeDataSource() {
|
||||
if(bds == null) {
|
||||
bds = new SimpleDataSource();
|
||||
String db = JDBC2Tests.getURL();
|
||||
if(db.indexOf('/') > -1) {
|
||||
db = db.substring(db.lastIndexOf('/')+1);
|
||||
} else if(db.indexOf(':') > -1) {
|
||||
db = db.substring(db.lastIndexOf(':')+1);
|
||||
}
|
||||
bds.setDatabaseName(db);
|
||||
bds.setUser(JDBC2Tests.getUser());
|
||||
bds.setPassword(JDBC2Tests.getPassword());
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user