From 7bf1c8b0ad5f2534362fb6938be28bb041d79c90 Mon Sep 17 00:00:00 2001 From: Barry Lind Date: Wed, 25 Sep 2002 07:01:31 +0000 Subject: [PATCH] Applied patch from Aaron Mulder (ammulder@alumni.princeton.edu) that fixes jdbc datasource support for jdk1.4/jdbc3 Modified Files: jdbc/build.xml jdbc/org/postgresql/Driver.java.in jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java jdbc/org/postgresql/jdbc2/optional/PoolingDataSource.java jdbc/org/postgresql/test/jdbc2/optional/BaseDataSourceTest.java jdbc/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java Added Files: jdbc/org/postgresql/jdbc3/Jdbc3ConnectionPool.java jdbc/org/postgresql/jdbc3/Jdbc3ObjectFactory.java jdbc/org/postgresql/jdbc3/Jdbc3PooledConnection.java jdbc/org/postgresql/jdbc3/Jdbc3PoolingDataSource.java jdbc/org/postgresql/jdbc3/Jdbc3SimpleDataSource.java jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java jdbc/org/postgresql/test/jdbc3/Jdbc3ConnectionPoolTest.java jdbc/org/postgresql/test/jdbc3/Jdbc3PoolingDataSourceTest.java jdbc/org/postgresql/test/jdbc3/Jdbc3SimpleDataSourceTest.java jdbc/org/postgresql/test/util/MiniJndiContext.java jdbc/org/postgresql/test/util/MiniJndiContextFactory.java --- src/interfaces/jdbc/build.xml | 4 +- .../jdbc/org/postgresql/Driver.java.in | 2 +- .../jdbc2/optional/BaseDataSource.java | 13 +- .../jdbc2/optional/PGObjectFactory.java | 6 +- .../jdbc2/optional/PooledConnectionImpl.java | 4 +- .../jdbc2/optional/PoolingDataSource.java | 35 ++- .../postgresql/jdbc3/Jdbc3ConnectionPool.java | 61 +++++ .../postgresql/jdbc3/Jdbc3ObjectFactory.java | 80 ++++++ .../jdbc3/Jdbc3PooledConnection.java | 20 ++ .../jdbc3/Jdbc3PoolingDataSource.java | 96 ++++++++ .../jdbc3/Jdbc3SimpleDataSource.java | 30 +++ .../jdbc2/optional/BaseDataSourceTest.java | 50 +++- .../jdbc2/optional/OptionalTestSuite.java | 3 +- .../jdbc2/optional/PoolingDataSourceTest.java | 106 ++++++++ .../test/jdbc3/Jdbc3ConnectionPoolTest.java | 67 +++++ .../jdbc3/Jdbc3PoolingDataSourceTest.java | 155 ++++++++++++ .../test/jdbc3/Jdbc3SimpleDataSourceTest.java | 60 +++++ .../postgresql/test/jdbc3/Jdbc3TestSuite.java | 12 +- .../postgresql/test/util/MiniJndiContext.java | 228 ++++++++++++++++++ .../test/util/MiniJndiContextFactory.java | 22 ++ 20 files changed, 1023 insertions(+), 31 deletions(-) create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ConnectionPool.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ObjectFactory.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PooledConnection.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PoolingDataSource.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3SimpleDataSource.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3ConnectionPoolTest.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3PoolingDataSourceTest.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3SimpleDataSourceTest.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContext.java create mode 100644 src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContextFactory.java diff --git a/src/interfaces/jdbc/build.xml b/src/interfaces/jdbc/build.xml index 80f9ca9b637..96c3dbb3138 100644 --- a/src/interfaces/jdbc/build.xml +++ b/src/interfaces/jdbc/build.xml @@ -6,7 +6,7 @@ This file now requires Ant 1.4.1. 2002-04-18 - $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/build.xml,v 1.28 2002/08/14 20:35:39 barry Exp $ + $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/build.xml,v 1.29 2002/09/25 07:01:30 barry Exp $ --> @@ -262,6 +262,7 @@ + @@ -278,6 +279,7 @@ + diff --git a/src/interfaces/jdbc/org/postgresql/Driver.java.in b/src/interfaces/jdbc/org/postgresql/Driver.java.in index 328c06ce6b9..0dbb0f04c0f 100644 --- a/src/interfaces/jdbc/org/postgresql/Driver.java.in +++ b/src/interfaces/jdbc/org/postgresql/Driver.java.in @@ -446,6 +446,6 @@ public class Driver implements java.sql.Driver } //The build number should be incremented for every new build - private static int m_buildNumber = 105; + private static int m_buildNumber = 106; } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java index c5fbde848ed..417cf87c10c 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java @@ -8,7 +8,7 @@ import java.sql.*; * Base class for data sources and related classes. * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ public abstract class BaseDataSource implements Referenceable { @@ -233,9 +233,18 @@ public abstract class BaseDataSource implements Referenceable return "jdbc:postgresql://" + serverName + (portNumber == 0 ? "" : ":" + portNumber) + "/" + databaseName; } + /** + * Generates a reference using the appropriate object factory. This + * implementation uses the JDBC 2 optional package object factory. + */ + protected Reference createReference() + { + return new Reference(getClass().getName(), PGObjectFactory.class.getName(), null); + } + public Reference getReference() throws NamingException { - Reference ref = new Reference(getClass().getName(), PGObjectFactory.class.getName(), null); + Reference ref = createReference(); ref.add(new StringRefAddr("serverName", serverName)); if (portNumber != 0) { diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java index 2ee3c10130b..48778e0e550 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java @@ -13,7 +13,7 @@ import java.util.Hashtable; * consistent. * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ public class PGObjectFactory implements ObjectFactory { @@ -81,7 +81,7 @@ public class PGObjectFactory implements ObjectFactory return loadBaseDataSource(cp, ref); } - private Object loadBaseDataSource(BaseDataSource ds, Reference ref) + protected Object loadBaseDataSource(BaseDataSource ds, Reference ref) { ds.setDatabaseName(getProperty(ref, "databaseName")); ds.setPassword(getProperty(ref, "password")); @@ -95,7 +95,7 @@ public class PGObjectFactory implements ObjectFactory return ds; } - private String getProperty(Reference ref, String s) + protected String getProperty(Reference ref, String s) { RefAddr addr = ref.get(s); if (addr == null) diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java index 4eb7d41b654..1d1d8f67df2 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java @@ -13,7 +13,7 @@ import java.lang.reflect.*; * @see ConnectionPool * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ public class PooledConnectionImpl implements PooledConnection { @@ -26,7 +26,7 @@ public class PooledConnectionImpl implements PooledConnection * Creates a new PooledConnection representing the specified physical * connection. */ - PooledConnectionImpl(Connection con, boolean autoCommit) + protected PooledConnectionImpl(Connection con, boolean autoCommit) { this.con = con; this.autoCommit = autoCommit; diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PoolingDataSource.java b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PoolingDataSource.java index 95ed5d8fef0..1d6c7daeac4 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PoolingDataSource.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/optional/PoolingDataSource.java @@ -33,7 +33,7 @@ import java.sql.SQLException; *

This implementation supports JDK 1.3 and higher.

* * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ public class PoolingDataSource extends BaseDataSource implements DataSource { @@ -45,7 +45,7 @@ public class PoolingDataSource extends BaseDataSource implements DataSource } // Additional Data Source properties - private String dataSourceName; + protected String dataSourceName; // Must be protected for subclasses to sync updates to it private int initialConnections = 0; private int maxConnections = 0; // State variables @@ -262,7 +262,7 @@ public class PoolingDataSource extends BaseDataSource implements DataSource * that number of connections will be created. After this method is called, * the DataSource properties cannot be changed. If you do not call this * explicitly, it will be called the first time you get a connection from the - * Datasource. + * DataSource. * @throws java.sql.SQLException * Occurs when the initialConnections is greater than zero, but the * DataSource is not able to create enough physical connections. @@ -271,7 +271,7 @@ public class PoolingDataSource extends BaseDataSource implements DataSource { synchronized (lock) { - source = new ConnectionPool(); + source = createConnectionPool(); source.setDatabaseName(getDatabaseName()); source.setPassword(getPassword()); source.setPortNumber(getPortNumber()); @@ -285,6 +285,17 @@ public class PoolingDataSource extends BaseDataSource implements DataSource } } + protected boolean isInitialized() { + return initialized; + } + + /** + * Creates the appropriate ConnectionPool to use for this DataSource. + */ + protected ConnectionPool createConnectionPool() { + return new ConnectionPool(); + } + /** * Gets a non-pooled connection, unless the user and password are the * same as the default values for this connection pool. @@ -358,13 +369,17 @@ public class PoolingDataSource extends BaseDataSource implements DataSource } used = null; } - synchronized (dataSources) - { - dataSources.remove(dataSourceName); - } - } + removeStoredDataSource(); + } - /** + protected void removeStoredDataSource() { + synchronized (dataSources) + { + dataSources.remove(dataSourceName); + } + } + + /** * Gets a connection from the pool. Will get an available one if * present, or create a new one if under the max limit. Will * block if all used and a new one would exceed the max. diff --git a/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ConnectionPool.java b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ConnectionPool.java new file mode 100644 index 00000000000..1f739d5eb9d --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ConnectionPool.java @@ -0,0 +1,61 @@ +package org.postgresql.jdbc3; + +import org.postgresql.jdbc2.optional.ConnectionPool; + +import javax.sql.PooledConnection; +import javax.naming.Reference; +import java.sql.SQLException; + +/** + * Jdbc3 implementation of ConnectionPoolDataSource. This is + * typically the interface used by an app server to interact + * with connection pools provided by a JDBC driver. PostgreSQL + * does not support XADataSource, the other common connection + * pooling interface (for connections supporting the two-phase + * commit protocol). + * + * @author Aaron Mulder (ammulder@alumni.princeton.edu) + * @version $Revision: 1.1 $ + */ +public class Jdbc3ConnectionPool extends ConnectionPool +{ + /** + * Gets a description of this DataSource. + */ + public String getDescription() + { + return "Jdbc3ConnectionPool from " + org.postgresql.Driver.getVersion(); + } + + /** + * Gets a connection which may be pooled by the app server or middleware + * implementation of DataSource. + * + * @throws java.sql.SQLException + * Occurs when the physical database connection cannot be established. + */ + public PooledConnection getPooledConnection() throws SQLException + { + return new Jdbc3PooledConnection(getConnection(), isDefaultAutoCommit()); + } + + /** + * Gets a connection which may be pooled by the app server or middleware + * implementation of DataSource. + * + * @throws java.sql.SQLException + * Occurs when the physical database connection cannot be established. + */ + public PooledConnection getPooledConnection(String user, String password) throws SQLException + { + return new Jdbc3PooledConnection(getConnection(user, password), isDefaultAutoCommit()); + } + + /** + * Generates a JDBC object factory reference. + */ + protected Reference createReference() + { + return new Reference(getClass().getName(), Jdbc3ObjectFactory.class.getName(), null); + } +} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ObjectFactory.java b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ObjectFactory.java new file mode 100644 index 00000000000..53151b80a24 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3ObjectFactory.java @@ -0,0 +1,80 @@ +package org.postgresql.jdbc3; + +import java.util.*; +import javax.naming.*; +import org.postgresql.jdbc2.optional.PGObjectFactory; + +/** + * JDBC3 version of the Object Factory used to recreate objects + * from their JNDI references. + * + * @author Aaron Mulder (ammulder@alumni.princeton.edu) + * @version $Revision: 1.1 $ + */ +public class Jdbc3ObjectFactory extends PGObjectFactory +{ + /** + * Dereferences a PostgreSQL DataSource. Other types of references are + * ignored. + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception + { + Reference ref = (Reference) obj; + if (ref.getClassName().equals(Jdbc3SimpleDataSource.class.getName())) + { + return loadSimpleDataSource(ref); + } + else if (ref.getClassName().equals(Jdbc3ConnectionPool.class.getName())) + { + return loadConnectionPool(ref); + } + else if (ref.getClassName().equals(Jdbc3PoolingDataSource.class.getName())) + { + return loadPoolingDataSource(ref); + } + else + { + return null; + } + } + + private Object loadPoolingDataSource(Reference ref) + { + // If DataSource exists, return it + String name = getProperty(ref, "dataSourceName"); + Jdbc3PoolingDataSource pds = Jdbc3PoolingDataSource.getDataSource(name); + if (pds != null) + { + return pds; + } + // Otherwise, create a new one + pds = new Jdbc3PoolingDataSource(); + pds.setDataSourceName(name); + loadBaseDataSource(pds, ref); + String min = getProperty(ref, "initialConnections"); + if (min != null) + { + pds.setInitialConnections(Integer.parseInt(min)); + } + String max = getProperty(ref, "maxConnections"); + if (max != null) + { + pds.setMaxConnections(Integer.parseInt(max)); + } + return pds; + } + + private Object loadSimpleDataSource(Reference ref) + { + Jdbc3SimpleDataSource ds = new Jdbc3SimpleDataSource(); + return loadBaseDataSource(ds, ref); + } + + private Object loadConnectionPool(Reference ref) + { + Jdbc3ConnectionPool cp = new Jdbc3ConnectionPool(); + return loadBaseDataSource(cp, ref); + } + +} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PooledConnection.java b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PooledConnection.java new file mode 100644 index 00000000000..cac966ddc81 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PooledConnection.java @@ -0,0 +1,20 @@ +package org.postgresql.jdbc3; + +import org.postgresql.jdbc2.optional.PooledConnectionImpl; + +import java.sql.Connection; + +/** + * JDBC3 implementation of PooledConnection, which manages + * a connection in a connection pool. + * + * @author Aaron Mulder (ammulder@alumni.princeton.edu) + * @version $Revision: 1.1 $ + */ +public class Jdbc3PooledConnection extends PooledConnectionImpl +{ + Jdbc3PooledConnection(Connection con, boolean autoCommit) + { + super(con, autoCommit); + } +} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PoolingDataSource.java b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PoolingDataSource.java new file mode 100644 index 00000000000..df3e50ad074 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3PoolingDataSource.java @@ -0,0 +1,96 @@ +package org.postgresql.jdbc3; + +import java.util.*; +import org.postgresql.jdbc2.optional.PoolingDataSource; +import org.postgresql.jdbc2.optional.ConnectionPool; + +import javax.naming.Reference; + +/** + * JDBC 3 implementation of a pooling DataSource. This is best + * used outside of an application server environment. Application + * servers generally prefer to deal with instances of + * ConnectionPoolDataSource (see ConnectionPool) or XADataSource + * (not available for PostgreSQL). + * + * @author Aaron Mulder (ammulder@alumni.princeton.edu) + * @version $Revision: 1.1 $ + */ +public class Jdbc3PoolingDataSource extends PoolingDataSource +{ + /** + * Store JDBC3 DataSources in different bucket than JDBC2 DataSources + */ + private static Map dataSources = new HashMap(); + + /** + * Store JDBC3 DataSources in different bucket than JDBC2 DataSources + */ + static Jdbc3PoolingDataSource getDataSource(String name) + { + return (Jdbc3PoolingDataSource) dataSources.get(name); + } + + /** + * Store JDBC3 DataSources in different bucket than JDBC2 DataSources + */ + protected void removeStoredDataSource() + { + synchronized (dataSources) + { + dataSources.remove(dataSourceName); + } + } + + /** + * Store JDBC3 DataSources in different bucket than JDBC2 DataSources + */ + public void setDataSourceName(String dataSourceName) + { + if (isInitialized()) + { + throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); + } + if (this.dataSourceName != null && dataSourceName != null && dataSourceName.equals(this.dataSourceName)) + { + return; + } + synchronized (dataSources) + { + if (getDataSource(dataSourceName) != null) + { + throw new IllegalArgumentException("DataSource with name '" + dataSourceName + "' already exists!"); + } + if (this.dataSourceName != null) + { + dataSources.remove(this.dataSourceName); + } + this.dataSourceName = dataSourceName; + dataSources.put(dataSourceName, this); + } + } + + /** + * Generates a JDBC3 object factory reference. + */ + protected Reference createReference() + { + return new Reference(getClass().getName(), Jdbc3ObjectFactory.class.getName(), null); + } + + /** + * Creates a JDBC3 ConnectionPool to use with this DataSource. + */ + protected ConnectionPool createConnectionPool() + { + return new Jdbc3ConnectionPool(); + } + + /** + * Gets a description of this DataSource. + */ + public String getDescription() + { + return "JDBC3 Pooling DataSource from " + org.postgresql.Driver.getVersion(); + } +} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3SimpleDataSource.java b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3SimpleDataSource.java new file mode 100644 index 00000000000..d8dff03221b --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc3/Jdbc3SimpleDataSource.java @@ -0,0 +1,30 @@ +package org.postgresql.jdbc3; + +import org.postgresql.jdbc2.optional.SimpleDataSource; + +import javax.naming.Reference; + +/** + * JDBC3 implementation of a non-pooling DataSource. + * + * @author Aaron Mulder (ammulder@alumni.princeton.edu) + * @version $Revision: 1.1 $ + */ +public class Jdbc3SimpleDataSource extends SimpleDataSource +{ + /** + * Generates a JDBC3 object factory reference. + */ + protected Reference createReference() + { + return new Reference(getClass().getName(), Jdbc3ObjectFactory.class.getName(), null); + } + + /** + * Gets a description of this DataSource. + */ + public String getDescription() + { + return "JDBC3 Non-Pooling DataSource from " + org.postgresql.Driver.getVersion(); + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/BaseDataSourceTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/BaseDataSourceTest.java index 7d041496c80..8add0b19d80 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/BaseDataSourceTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/BaseDataSourceTest.java @@ -2,10 +2,11 @@ package org.postgresql.test.jdbc2.optional; import junit.framework.TestCase; import org.postgresql.test.TestUtil; -import org.postgresql.jdbc2.optional.SimpleDataSource; import org.postgresql.jdbc2.optional.BaseDataSource; import java.sql.*; +import java.util.*; +import javax.naming.*; /** * Common tests for all the BaseDataSource implementations. This is @@ -15,10 +16,11 @@ import java.sql.*; * tests. * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.3 $ + * @version $Revision: 1.4 $ */ public abstract class BaseDataSourceTest extends TestCase { + public static String DATA_SOURCE_JNDI = "BaseDataSource"; protected Connection con; protected BaseDataSource bds; @@ -60,7 +62,10 @@ public abstract class BaseDataSourceTest extends TestCase */ protected Connection getDataSourceConnection() throws SQLException { - initializeDataSource(); + if(bds == null) + { + initializeDataSource(); + } return bds.getConnection(); } @@ -174,6 +179,21 @@ public abstract class BaseDataSourceTest extends TestCase } } + /** + * Uses the mini-JNDI implementation for testing purposes + */ + protected InitialContext getInitialContext() + { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "org.postgresql.test.util.MiniJndiContextFactory"); + try { + return new InitialContext(env); + } catch(NamingException e) { + fail("Unable to create InitialContext: "+e.getMessage()); + return null; + } + } + /** * Eventually, we must test stuffing the DataSource in JNDI and * then getting it back out and make sure it's still usable. This @@ -182,6 +202,26 @@ public abstract class BaseDataSourceTest extends TestCase */ public void testJndi() { - // TODO: Put the DS in JNDI, retrieve it, and try some of this stuff again - } + initializeDataSource(); + BaseDataSource oldbds = bds; + InitialContext ic = getInitialContext(); + try { + ic.rebind(DATA_SOURCE_JNDI, bds); + bds = (BaseDataSource)ic.lookup(DATA_SOURCE_JNDI); + assertTrue("Got null looking up DataSource from JNDI!", bds != null); + compareJndiDataSource(oldbds, bds); + } catch (NamingException e) { + fail(e.getMessage()); + } + oldbds = bds; + testUseConnection(); + assertTrue("Test should not have changed DataSource ("+bds+" != "+oldbds+")!", bds == oldbds); + } + + /** + * Check whether a DS was dereferenced from JNDI or recreated. + */ + protected void compareJndiDataSource(BaseDataSource oldbds, BaseDataSource bds) { + assertTrue("DataSource was dereferenced, should have been serialized or recreated", bds != oldbds); + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java index 49108910f90..6acb381da90 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java @@ -8,7 +8,7 @@ import junit.framework.TestSuite; * PooledConnection implementations. * * @author Aaron Mulder (ammulder@chariotsolutions.com) - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ public class OptionalTestSuite extends TestSuite { @@ -21,6 +21,7 @@ public class OptionalTestSuite extends TestSuite TestSuite suite = new TestSuite(); suite.addTestSuite(SimpleDataSourceTest.class); suite.addTestSuite(ConnectionPoolTest.class); + suite.addTestSuite(ConnectionPoolTest.class); return suite; } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java new file mode 100644 index 00000000000..9b08eca7d0c --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java @@ -0,0 +1,106 @@ +package org.postgresql.test.jdbc2.optional; + +import java.sql.SQLException; +import org.postgresql.test.TestUtil; +import org.postgresql.jdbc2.optional.PoolingDataSource; +import org.postgresql.jdbc2.optional.BaseDataSource; + +/** + * Minimal tests for pooling DataSource. Needs many more. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class PoolingDataSourceTest extends BaseDataSourceTest +{ + private final static String DS_NAME = "JDBC 2 SE Test DataSource"; + + /** + * Constructor required by JUnit + */ + public PoolingDataSourceTest(String name) + { + super(name); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + if (bds instanceof PoolingDataSource) + { + ((PoolingDataSource) bds).close(); + } + } + + /** + * Creates and configures a new SimpleDataSource. + */ + protected void initializeDataSource() + { + if (bds == null) + { + bds = new PoolingDataSource(); + String db = TestUtil.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(TestUtil.getUser()); + bds.setPassword(TestUtil.getPassword()); + ((PoolingDataSource) bds).setDataSourceName(DS_NAME); + ((PoolingDataSource) bds).setInitialConnections(2); + ((PoolingDataSource) bds).setMaxConnections(10); + } + } + + /** + * In this case, we *do* want it to be pooled. + */ + public void testNotPooledConnection() + { + try + { + con = getDataSourceConnection(); + String name = con.toString(); + con.close(); + con = getDataSourceConnection(); + String name2 = con.toString(); + con.close(); + assertTrue("Pooled DS doesn't appear to be pooling connections!", name.equals(name2)); + } + catch (SQLException e) + { + fail(e.getMessage()); + } + } + + /** + * In this case, the desired behavior is dereferencing. + */ + protected void compareJndiDataSource(BaseDataSource oldbds, BaseDataSource bds) + { + assertTrue("DataSource was serialized or recreated, should have been dereferenced", bds == oldbds); + } + + /** + * Check that 2 DS instances can't use the same name. + */ + public void testCantReuseName() + { + initializeDataSource(); + PoolingDataSource pds = new PoolingDataSource(); + try + { + pds.setDataSourceName(DS_NAME); + fail("Should have denied 2nd DataSource with same name"); + } + catch (IllegalArgumentException e) + { + } + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3ConnectionPoolTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3ConnectionPoolTest.java new file mode 100644 index 00000000000..a171e8ff025 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3ConnectionPoolTest.java @@ -0,0 +1,67 @@ +package org.postgresql.test.jdbc3; + +import java.sql.Connection; +import java.sql.SQLException; +import javax.sql.PooledConnection; +import org.postgresql.test.jdbc2.optional.ConnectionPoolTest; +import org.postgresql.test.TestUtil; +import org.postgresql.jdbc3.*; + +/** + * Tests JDBC3 implementation of ConnectionPoolDataSource. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class Jdbc3ConnectionPoolTest extends ConnectionPoolTest +{ + public Jdbc3ConnectionPoolTest(String name) + { + super(name); + } + + /** + * Creates and configures a Jdbc3ConnectionPool + */ + protected void initializeDataSource() + { + if (bds == null) + { + bds = new Jdbc3ConnectionPool(); + String db = TestUtil.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(TestUtil.getUser()); + bds.setPassword(TestUtil.getPassword()); + } + } + + /** + * Makes sure this is a JDBC 3 implementation producing JDBC3 + * connections. Depends on toString implementation of + * connection wrappers. + */ + public void testConfirmJdbc3Impl() + { + try + { + initializeDataSource(); + assertTrue("Wrong ConnectionPool impl used by test: " + bds.getClass().getName(), bds instanceof Jdbc3ConnectionPool); + PooledConnection pc = ((Jdbc3ConnectionPool) bds).getPooledConnection(); + assertTrue("Wrong PooledConnection impl generated by JDBC3 ConnectionPoolDataSource: " + pc.getClass().getName(), pc instanceof Jdbc3PooledConnection); + assertTrue("Wrong Connnection class used in JDBC3 ConnectionPoolDataSource's PooledConnection impl: " + pc.getConnection().toString(), pc.getConnection().toString().indexOf("Jdbc3") > -1); + pc.close(); + } + catch (SQLException e) + { + fail(e.getMessage()); + } + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3PoolingDataSourceTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3PoolingDataSourceTest.java new file mode 100644 index 00000000000..55374cb1b81 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3PoolingDataSourceTest.java @@ -0,0 +1,155 @@ +package org.postgresql.test.jdbc3; + +import java.sql.Connection; +import java.sql.SQLException; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import org.postgresql.test.jdbc2.optional.PoolingDataSourceTest; +import org.postgresql.test.TestUtil; +import org.postgresql.jdbc3.Jdbc3PoolingDataSource; +import org.postgresql.jdbc2.optional.PoolingDataSource; + +/** + * Minimal tests for JDBC3 pooling DataSource. Needs many more. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class Jdbc3PoolingDataSourceTest extends PoolingDataSourceTest +{ + private final static String DS_NAME = "JDBC 3 Test DataSource"; + + /** + * Constructor required by JUnit + */ + public Jdbc3PoolingDataSourceTest(String name) + { + super(name); + } + + /** + * Creates and configures a new SimpleDataSource. + */ + protected void initializeDataSource() + { + if (bds == null) + { + bds = new Jdbc3PoolingDataSource(); + configureDataSource((Jdbc3PoolingDataSource) bds); + } + } + + private void configureDataSource(PoolingDataSource source) + { + String db = TestUtil.getURL(); + if (db.indexOf('/') > -1) + { + db = db.substring(db.lastIndexOf('/') + 1); + } + else if (db.indexOf(':') > -1) + { + db = db.substring(db.lastIndexOf(':') + 1); + } + source.setDatabaseName(db); + source.setUser(TestUtil.getUser()); + source.setPassword(TestUtil.getPassword()); + source.setDataSourceName(DS_NAME); + source.setInitialConnections(2); + source.setMaxConnections(10); + } + + /** + * Check that 2 DS instances can't use the same name. + */ + public void testCantReuseName() + { + initializeDataSource(); + Jdbc3PoolingDataSource pds = new Jdbc3PoolingDataSource(); + try + { + pds.setDataSourceName(DS_NAME); + fail("Should have denied 2nd DataSource with same name"); + } + catch (IllegalArgumentException e) + { + } + } + + /** + * Test that JDBC 2 and JDBC 3 DSs come from different buckets + * as far as creating with the same name + */ + public void testDifferentImplPools() + { + initializeDataSource(); + PoolingDataSource pds = new PoolingDataSource(); + try + { + configureDataSource(pds); + PoolingDataSource p2 = new PoolingDataSource(); + try + { + configureDataSource(p2); + fail("Shouldn't be able to create 2 JDBC 2 DSs with same name"); + } + catch (IllegalArgumentException e) + { + } + Jdbc3PoolingDataSource p3 = new Jdbc3PoolingDataSource(); + try + { + configureDataSource(p3); + fail("Shouldn't be able to create 2 JDBC 3 DSs with same name"); + } + catch (IllegalArgumentException e) + { + } + } + finally + { + pds.close(); + } + } + + /** + * Test that JDBC 2 and JDBC 3 DSs come from different buckets + * as far as fetching from JNDI + */ + public void testDifferentImplJndi() + { + initializeDataSource(); + PoolingDataSource pds = new PoolingDataSource(); + try + { + configureDataSource(pds); + try + { + Connection j3c = getDataSourceConnection(); + Connection j2c = pds.getConnection(); + j2c.close(); + j3c.close(); + InitialContext ctx = getInitialContext(); + ctx.bind("JDBC2", pds); + ctx.bind("JDBC3", bds); + pds = (PoolingDataSource) ctx.lookup("JDBC2"); + bds = (Jdbc3PoolingDataSource) ctx.lookup("JDBC3"); + j2c = pds.getConnection(); + j3c = bds.getConnection(); + j2c.close(); + j3c.close(); + } + catch (SQLException e) + { + fail(e.getMessage()); + } + catch (NamingException e) + { + fail(e.getMessage()); + } + } + finally + { + pds.close(); + } + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3SimpleDataSourceTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3SimpleDataSourceTest.java new file mode 100644 index 00000000000..cef4c83f651 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3SimpleDataSourceTest.java @@ -0,0 +1,60 @@ +package org.postgresql.test.jdbc3; + +import java.sql.Connection; +import java.sql.SQLException; +import org.postgresql.test.jdbc2.optional.SimpleDataSourceTest; +import org.postgresql.test.TestUtil; +import org.postgresql.jdbc3.*; + +/** + * Tests JDBC3 non-pooling DataSource. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class Jdbc3SimpleDataSourceTest extends SimpleDataSourceTest { + /** + * Constructor required by JUnit + */ + public Jdbc3SimpleDataSourceTest(String name) + { + super(name); + } + + /** + * Creates and configures a new SimpleDataSource. + */ + protected void initializeDataSource() + { + if (bds == null) + { + bds = new Jdbc3SimpleDataSource(); + String db = TestUtil.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(TestUtil.getUser()); + bds.setPassword(TestUtil.getPassword()); + } + } + /** + * Makes sure this is a JDBC 3 implementation producing JDBC3 + * connections. + */ + public void testConfirmJdbc3Impl() + { + try { + Connection con = getDataSourceConnection(); + assertTrue("Wrong SimpleDataSource impl used by test: "+bds.getClass().getName(), bds instanceof Jdbc3SimpleDataSource); + assertTrue("Wrong Connnection class generated by JDBC3 DataSource: "+con.getClass().getName(), con instanceof Jdbc3Connection); + } catch (SQLException e) { + fail(e.getMessage()); + } + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java index 5278977de46..ec560213a9e 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java @@ -1,10 +1,6 @@ package org.postgresql.test.jdbc3; import junit.framework.TestSuite; -import junit.framework.TestCase; -import junit.framework.Test; - -import java.sql.*; /* * Executes all known tests for JDBC3 @@ -17,7 +13,11 @@ public class Jdbc3TestSuite extends TestSuite */ public static TestSuite suite() { - //Currently there are no specific jdbc3 tests so just run the jdbc2 tests - return org.postgresql.test.jdbc2.Jdbc2TestSuite.suite(); + TestSuite suite = new TestSuite(); + suite.addTest(org.postgresql.test.jdbc2.Jdbc2TestSuite.suite()); + suite.addTestSuite(Jdbc3SimpleDataSourceTest.class); + suite.addTestSuite(Jdbc3ConnectionPoolTest.class); + suite.addTestSuite(Jdbc3PoolingDataSourceTest.class); + return suite; } } diff --git a/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContext.java b/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContext.java new file mode 100644 index 00000000000..4caf24120c5 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContext.java @@ -0,0 +1,228 @@ +package org.postgresql.test.util; + +import java.util.*; +import java.rmi.MarshalledObject; +import java.io.Serializable; +import javax.naming.*; +import javax.naming.spi.ObjectFactory; + +/** + * The Context for a trivial JNDI implementation. This is not meant to + * be very useful, beyond testing JNDI features of the connection + * pools. It is not a complete JNDI implementations. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class MiniJndiContext implements Context +{ + private Map map = new HashMap(); + + public MiniJndiContext() + { + } + + public Object lookup(Name name) throws NamingException + { + return lookup(name.get(0)); + } + + public Object lookup(String name) throws NamingException + { + Object o = map.get(name); + if (o == null) + { + return null; + } + if (o instanceof Reference) + { + Reference ref = (Reference) o; + try + { + Class factoryClass = Class.forName(ref.getFactoryClassName()); + ObjectFactory fac = (ObjectFactory) factoryClass.newInstance(); + Object result = fac.getObjectInstance(ref, null, this, null); + return result; + } + catch (Exception e) + { + throw new NamingException("Unable to dereference to object: " + e); + } + } + else if (o instanceof MarshalledObject) + { + try + { + Object result = ((MarshalledObject) o).get(); + return result; + } + catch (java.io.IOException e) + { + throw new NamingException("Unable to deserialize object: " + e); + } + catch (ClassNotFoundException e) + { + throw new NamingException("Unable to deserialize object: " + e); + } + } + else + { + throw new NamingException("JNDI Object is neither Referenceable nor Serializable"); + } + } + + public void bind(Name name, Object obj) throws NamingException + { + rebind(name.get(0), obj); + } + + public void bind(String name, Object obj) throws NamingException + { + rebind(name, obj); + } + + public void rebind(Name name, Object obj) throws NamingException + { + rebind(name.get(0), obj); + } + + public void rebind(String name, Object obj) throws NamingException + { + if (obj instanceof Referenceable) + { + Reference ref = ((Referenceable) obj).getReference(); + map.put(name, ref); + } + else if (obj instanceof Serializable) + { + try + { + MarshalledObject mo = new MarshalledObject(obj); + map.put(name, mo); + } + catch (java.io.IOException e) + { + throw new NamingException("Unable to serialize object to JNDI: " + e); + } + } + else + { + throw new NamingException("Object to store in JNDI is neither Referenceable nor Serializable"); + } + } + + public void unbind(Name name) throws NamingException + { + unbind(name.get(0)); + } + + public void unbind(String name) throws NamingException + { + map.remove(name); + } + + public void rename(Name oldName, Name newName) throws NamingException + { + rename(oldName.get(0), newName.get(0)); + } + + public void rename(String oldName, String newName) throws NamingException + { + map.put(newName, map.remove(oldName)); + } + + public NamingEnumeration list(Name name) throws NamingException + { + return null; + } + + public NamingEnumeration list(String name) throws NamingException + { + return null; + } + + public NamingEnumeration listBindings(Name name) throws NamingException + { + return null; + } + + public NamingEnumeration listBindings(String name) throws NamingException + { + return null; + } + + public void destroySubcontext(Name name) throws NamingException + { + } + + public void destroySubcontext(String name) throws NamingException + { + } + + public Context createSubcontext(Name name) throws NamingException + { + return null; + } + + public Context createSubcontext(String name) throws NamingException + { + return null; + } + + public Object lookupLink(Name name) throws NamingException + { + return null; + } + + public Object lookupLink(String name) throws NamingException + { + return null; + } + + public NameParser getNameParser(Name name) throws NamingException + { + return null; + } + + public NameParser getNameParser(String name) throws NamingException + { + return null; + } + + public Name composeName(Name name, Name prefix) throws NamingException + { + return null; + } + + public String composeName(String name, String prefix) + throws NamingException + { + return null; + } + + public Object addToEnvironment(String propName, Object propVal) + throws NamingException + { + return null; + } + + public Object removeFromEnvironment(String propName) + throws NamingException + { + return null; + } + + public Hashtable getEnvironment() throws NamingException + { + return null; + } + + public void close() throws NamingException + { + } + + public String getNameInNamespace() throws NamingException + { + return null; + } +} diff --git a/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContextFactory.java b/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContextFactory.java new file mode 100644 index 00000000000..58cc533ce0e --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/test/util/MiniJndiContextFactory.java @@ -0,0 +1,22 @@ +package org.postgresql.test.util; + +import java.util.*; +import javax.naming.*; +import javax.naming.spi.InitialContextFactory; + +/** + * The ICF for a trivial JNDI implementation. This is not meant to + * be very useful, beyond testing JNDI features of the connection + * pools. + * + * @author Aaron Mulder (ammulder@chariotsolutions.com) + * @version $Revision: 1.1 $ + */ +public class MiniJndiContextFactory implements InitialContextFactory +{ + public Context getInitialContext(Hashtable environment) + throws NamingException + { + return new MiniJndiContext(); + } +}