postgresql-jdbc.git / summary / log / commit / refs
commit 2e146b900f08b7f96316147e69ceb2e69efb79a2
Author: Kris Jurka <books@ejurka.com>
Date: Wed Nov 29 04:03:48 2006 +0000
Missed adding these files in previous commit to build datasource code
under JDK1.6.
.../ds/jdbc23/AbstractJdbc23PooledConnection.java | 480 +++++++++++++++++++
.../ds/jdbc23/AbstractJdbc23PoolingDataSource.java | 515 +++++++++++++++++++++
.../ds/jdbc23/AbstractJdbc23SimpleDataSource.java | 47 ++
3 files changed, 1042 insertions(+)
diff --git a/org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java b/org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java
new file mode 100644
index 00000000..69141fa5
--- /dev/null
+++ b/org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java
@@ -0,0 +1,480 @@
+/*-------------------------------------------------------------------------
+*
+* Copyright (c) 2004-2005, PostgreSQL Global Development Group
+*
+* IDENTIFICATION
+* $PostgreSQL$
+*
+*-------------------------------------------------------------------------
+*/
+package org.postgresql.ds.jdbc23;
+
+import javax.sql.*;
+import java.sql.*;
+import java.util.*;
+import java.lang.reflect.*;
+import org.postgresql.PGConnection;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+/**
+ * PostgreSQL implementation of the PooledConnection interface. This shouldn't
+ * be used directly, as the pooling client should just interact with the
+ * ConnectionPool instead.
+ * @see org.postgresql.ds.PGConnectionPoolDataSource
+ *
+ * @author Aaron Mulder (ammulder@chariotsolutions.com)
+ * @author Csaba Nagy (ncsaba@yahoo.com)
+ */
+public abstract class AbstractJdbc23PooledConnection
+{
+ private List listeners = new LinkedList();
+ private Connection con;
+ private ConnectionHandler last;
+ private final boolean autoCommit;
+ private final boolean isXA;
+
+ /**
+ * Creates a new PooledConnection representing the specified physical
+ * connection.
+ */
+ public AbstractJdbc23PooledConnection(Connection con, boolean autoCommit, boolean isXA)
+ {
+ this.con = con;
+ this.autoCommit = autoCommit;
+ this.isXA = isXA;
+ }
+
+ /**
+ * Adds a listener for close or fatal error events on the connection
+ * handed out to a client.
+ */
+ public void addConnectionEventListener(ConnectionEventListener connectionEventListener)
+ {
+ listeners.add(connectionEventListener);
+ }
+
+ /**
+ * Removes a listener for close or fatal error events on the connection
+ * handed out to a client.
+ */
+ public void removeConnectionEventListener(ConnectionEventListener connectionEventListener)
+ {
+ listeners.remove(connectionEventListener);
+ }
+
+ /**
+ * Closes the physical database connection represented by this
+ * PooledConnection. If any client has a connection based on
+ * this PooledConnection, it is forcibly closed as well.
+ */
+ public void close() throws SQLException
+ {
+ if (last != null)
+ {
+ last.close();
+ if (!con.getAutoCommit())
+ {
+ try
+ {
+ con.rollback();
+ }
+ catch (SQLException e)
+ {
+ }
+ }
+ }
+ try
+ {
+ con.close();
+ }
+ finally
+ {
+ con = null;
+ }
+ }
+
+ /**
+ * Gets a handle for a client to use. This is a wrapper around the
+ * physical connection, so the client can call close and it will just
+ * return the connection to the pool without really closing the
+ * pgysical connection.
+ *
+ * <p>According to the JDBC 2.0 Optional Package spec (6.2.3), only one
+ * client may have an active handle to the connection at a time, so if
+ * there is a previous handle active when this is called, the previous
+ * one is forcibly closed and its work rolled back.</p>
+ */
+ public Connection getConnection() throws SQLException
+ {
+ if (con == null)
+ {
+ // Before throwing the exception, let's notify the registered listeners about the error
+ PSQLException sqlException = new PSQLException(GT.tr("This PooledConnection has already been closed."),
+ PSQLState.CONNECTION_DOES_NOT_EXIST);
+ fireConnectionFatalError(sqlException);
+ throw sqlException;
+ }
+ // If any error occures while opening a new connection, the listeners
+ // have to be notified. This gives a chance to connection pools to
+ // elliminate bad pooled connections.
+ try
+ {
+ // Only one connection can be open at a time from this PooledConnection. See JDBC 2.0 Optional Package spec section 6.2.3
+ if (last != null)
+ {
+ last.close();
+ if (!con.getAutoCommit())
+ {
+ try
+ {
+ con.rollback();
+ }
+ catch (SQLException e)
+ {
+ }
+ }
+ con.clearWarnings();
+ }
+ con.setAutoCommit(autoCommit);
+ }
+ catch (SQLException sqlException)
+ {
+ fireConnectionFatalError(sqlException);
+ throw (SQLException)sqlException.fillInStackTrace();
+ }
+ ConnectionHandler handler = new ConnectionHandler(con);
+ last = handler;
+
+ Connection proxyCon = (Connection)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class, PGConnection.class}, handler);
+ last.setProxy(proxyCon);
+ return proxyCon;
+ }
+
+ /**
+ * Used to fire a connection closed event to all listeners.
+ */
+ void fireConnectionClosed()
+ {
+ ConnectionEvent evt = null;
+ // Copy the listener list so the listener can remove itself during this method call
+ ConnectionEventListener[] local = (ConnectionEventListener[]) listeners.toArray(new ConnectionEventListener[listeners.size()]);
+ for (int i = 0; i < local.length; i++)
+ {
+ ConnectionEventListener listener = local[i];
+ if (evt == null)
+ {
+ evt = createConnectionEvent(null);
+ }
+ listener.connectionClosed(evt);
+ }
+ }
+
+ /**
+ * Used to fire a connection error event to all listeners.
+ */
+ void fireConnectionFatalError(SQLException e)
+ {
+ ConnectionEvent evt = null;
+ // Copy the listener list so the listener can remove itself during this method call
+ ConnectionEventListener[] local = (ConnectionEventListener[])listeners.toArray(new ConnectionEventListener[listeners.size()]);
+ for (int i = 0; i < local.length; i++)
+ {
+ ConnectionEventListener listener = local[i];
+ if (evt == null)
+ {
+ evt = createConnectionEvent(e);
+ }
+ listener.connectionErrorOccurred(evt);
+ }
+ }
+
+ protected abstract ConnectionEvent createConnectionEvent(SQLException e);
+
+ // Classes we consider fatal.
+ private static String[] fatalClasses = {
+ "08", // connection error
+ "53", // insufficient resources
+
+ // nb: not just "57" as that includes query cancel which is nonfatal
+ "57P01", // admin shutdown
+ "57P02", // crash shutdown
+ "57P03", // cannot connect now
+
+ "58", // system error (backend)
+ "60", // system error (driver)
+ "99", // unexpected error
+ "F0", // configuration file error (backend)
+ "XX", // internal error (backend)
+ };
+
+ private static boolean isFatalState(String state) {
+ if (state == null) // no info, assume fatal
+ return true;
+ if (state.length() < 2) // no class info, assume fatal
+ return true;
+
+ for (int i = 0; i < fatalClasses.length; ++i)
+ if (state.startsWith(fatalClasses[i]))
+ return true; // fatal
+
+ return false;
+ }
+
+ /**
+ * Fires a connection error event, but only if we
+ * think the exception is fatal.
+ *
+ * @param e the SQLException to consider
+ */
+ private void fireConnectionError(SQLException e)
+ {
+ if (!isFatalState(e.getSQLState()))
+ return;
+
+ fireConnectionFatalError(e);
+ }
+
+ /**
+ * Instead of declaring a class implementing Connection, which would have
+ * to be updated for every JDK rev, use a dynamic proxy to handle all
+ * calls through the Connection interface. This is the part that
+ * requires JDK 1.3 or higher, though JDK 1.2 could be supported with a
+ * 3rd-party proxy package.
+ */
+ private class ConnectionHandler implements InvocationHandler
+ {
+ private Connection con;
+ private Connection proxy; // the Connection the client is currently using, which is a proxy
+ private boolean automatic = false;
+
+ public ConnectionHandler(Connection con)
+ {
+ this.con = con;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ // From Object
+ if (method.getDeclaringClass().getName().equals("java.lang.Object"))
+ {
+ if (method.getName().equals("toString"))
+ {
+ return "Pooled connection wrapping physical connection " + con;
+ }
+ if (method.getName().equals("hashCode"))
+ {
+ return new Integer(con.hashCode());
+ }
+ if (method.getName().equals("equals"))
+ {
+ if (args[0] == null)
+ {
+ return Boolean.FALSE;
+ }
+ try
+ {
+ return Proxy.isProxyClass(args[0].getClass()) && ((ConnectionHandler) Proxy.getInvocationHandler(args[0])).con == con ? Boolean.TRUE : Boolean.FALSE;
+ }
+ catch (ClassCastException e)
+ {
+ return Boolean.FALSE;
+ }
+ }
+ try
+ {
+ return method.invoke(con, args);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw e.getTargetException();
+ }
+ }
+ // All the rest is from the Connection or PGConnection interface
+ if (method.getName().equals("isClosed"))
+ {
+ return con == null ? Boolean.TRUE : Boolean.FALSE;
+ }
+ if (con == null && !method.getName().equals("close"))
+ {
+ throw new PSQLException(automatic ? GT.tr("Connection has been closed automatically because a new connection was opened for the same PooledConnection or the PooledConnection has been closed.") : GT.tr("Connection has been closed."),
+ PSQLState.CONNECTION_DOES_NOT_EXIST);
+ }
+ if (method.getName().equals("close"))
+ {
+ // we are already closed and a double close
+ // is not an error.
+ if (con == null)
+ return null;
+
+ SQLException ex = null;
+ if (!isXA && !con.getAutoCommit())
+ {
+ try
+ {
+ con.rollback();
+ }
+ catch (SQLException e)
+ {
+ ex = e;
+ }
+ }
+ con.clearWarnings();
+ con = null;
+ proxy = null;
+ last = null;
+ fireConnectionClosed();
+ if (ex != null)
+ {
+ throw ex;
+ }
+ return null;
+ }
+
+ // From here on in, we invoke via reflection, catch exceptions,
+ // and check if they're fatal before rethrowing.
+
+ try {
+ if (method.getName().equals("createStatement"))
+ {
+ Statement st = (Statement)method.invoke(con, args);
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Statement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
+ }
+ else if (method.getName().equals("prepareCall"))
+ {
+ Statement st = (Statement)method.invoke(con, args);
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{CallableStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
+ }
+ else if (method.getName().equals("prepareStatement"))
+ {
+ Statement st = (Statement)method.invoke(con, args);
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{PreparedStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st));
+ }
+ else
+ {
+ return method.invoke(con, args);
+ }
+ } catch (InvocationTargetException e) {
+ Throwable te = e.getTargetException();
+ if (te instanceof SQLException)
+ fireConnectionError((SQLException)te); // Tell listeners about exception if it's fatal
+ throw te;
+ }
+ }
+
+ Connection getProxy() {
+ return proxy;
+ }
+
+ void setProxy(Connection proxy) {
+ this.proxy = proxy;
+ }
+
+ public void close()
+ {
+ if (con != null)
+ {
+ automatic = true;
+ }
+ con = null;
+ proxy = null;
+ // No close event fired here: see JDBC 2.0 Optional Package spec section 6.3
+ }
+
+ public boolean isClosed() {
+ return con == null;
+ }
+ }
+
+ /**
+ * Instead of declaring classes implementing Statement, PreparedStatement,
+ * and CallableStatement, which would have to be updated for every JDK rev,
+ * use a dynamic proxy to handle all calls through the Statement
+ * interfaces. This is the part that requires JDK 1.3 or higher, though
+ * JDK 1.2 could be supported with a 3rd-party proxy package.
+ *
+ * The StatementHandler is required in order to return the proper
+ * Connection proxy for the getConnection method.
+ */
+ private class StatementHandler implements InvocationHandler {
+ private AbstractJdbc23PooledConnection.ConnectionHandler con;
+ private Statement st;
+
+ public StatementHandler(AbstractJdbc23PooledConnection.ConnectionHandler con, Statement st) {
+ this.con = con;
+ this.st = st;
+ }
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ // From Object
+ if (method.getDeclaringClass().getName().equals("java.lang.Object"))
+ {
+ if (method.getName().equals("toString"))
+ {
+ return "Pooled statement wrapping physical statement " + st;
+ }
+ if (method.getName().equals("hashCode"))
+ {
+ return new Integer(st.hashCode());
+ }
+ if (method.getName().equals("equals"))
+ {
+ if (args[0] == null)
+ {
+ return Boolean.FALSE;
+ }
+ try
+ {
+ return Proxy.isProxyClass(args[0].getClass()) && ((StatementHandler) Proxy.getInvocationHandler(args[0])).st == st ? Boolean.TRUE : Boolean.FALSE;
+ }
+ catch (ClassCastException e)
+ {
+ return Boolean.FALSE;
+ }
+ }
+ return method.invoke(st, args);
+ }
+ // All the rest is from the Statement interface
+ if (method.getName().equals("close"))
+ {
+ // closing an already closed object is a no-op
+ if (st == null || con.isClosed())
+ return null;
+
+ try
+ {
+ st.close();
+ }
+ finally
+ {
+ con = null;
+ st = null;
+ }
+ return null;
+ }
+ if (st == null || con.isClosed())
+ {
+ throw new PSQLException(GT.tr("Statement has been closed."),
+ PSQLState.OBJECT_NOT_IN_STATE);
+ }
+
+ if (method.getName().equals("getConnection"))
+ {
+ return con.getProxy(); // the proxied connection, not a physical connection
+ }
+
+ try
+ {
+ return method.invoke(st, args);
+ } catch (InvocationTargetException e) {
+ Throwable te = e.getTargetException();
+ if (te instanceof SQLException)
+ fireConnectionError((SQLException)te); // Tell listeners about exception if it's fatal
+ throw te;
+ }
+ }
+ }
+}
diff --git a/org/postgresql/ds/jdbc23/AbstractJdbc23PoolingDataSource.java b/org/postgresql/ds/jdbc23/AbstractJdbc23PoolingDataSource.java
new file mode 100644
index 00000000..469d60ac
--- /dev/null
+++ b/org/postgresql/ds/jdbc23/AbstractJdbc23PoolingDataSource.java
@@ -0,0 +1,515 @@
+/*-------------------------------------------------------------------------
+*
+* Copyright (c) 2004-2005, PostgreSQL Global Development Group
+*
+* IDENTIFICATION
+* $PostgreSQL$
+*
+*-------------------------------------------------------------------------
+*/
+package org.postgresql.ds.jdbc23;
+
+import javax.sql.*;
+import javax.naming.*;
+import java.util.*;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLState;
+import org.postgresql.util.PSQLException;
+import org.postgresql.ds.*;
+import org.postgresql.ds.common.*;
+
+/**
+ * DataSource which uses connection pooling. <font color="red">Don't use this if
+ * your server/middleware vendor provides a connection pooling implementation
+ * which interfaces with the PostgreSQL ConnectionPoolDataSource implementation!</font>
+ * This class is provided as a convenience, but the JDBC Driver is really not
+ * supposed to handle the connection pooling algorithm. Instead, the server or
+ * middleware product is supposed to handle the mechanics of connection pooling,
+ * and use the PostgreSQL implementation of ConnectionPoolDataSource to provide
+ * the connections to pool.
+ *
+ * <p>If you're sure you want to use this, then you must set the properties
+ * dataSourceName, databaseName, user, and password (if required for the user).
+ * The settings for serverName, portNumber, initialConnections, and
+ * maxConnections are optional. Note that <i>only connections
+ * for the default user will be pooled!</i> Connections for other users will
+ * be normal non-pooled connections, and will not count against the maximum pool
+ * size limit.</p>
+ *
+ * <p>If you put this DataSource in JNDI, and access it from different JVMs (or
+ * otherwise load this class from different ClassLoaders), you'll end up with one
+ * pool per ClassLoader or VM. This is another area where a server-specific
+ * implementation may provide advanced features, such as using a single pool
+ * across all VMs in a cluster.</p>
+ *
+ * <p>This implementation supports JDK 1.3 and higher.</p>
+ *
+ * @author Aaron Mulder (ammulder@chariotsolutions.com)
+ */
+public abstract class AbstractJdbc23PoolingDataSource extends BaseDataSource
+{
+ protected static Map dataSources = new HashMap();
+
+ public static PGPoolingDataSource getDataSource(String name)
+ {
+ return (PGPoolingDataSource)dataSources.get(name);
+ }
+
+ // Additional Data Source properties
+ protected String dataSourceName; // Must be protected for subclasses to sync updates to it
+ private int initialConnections = 0;
+ private int maxConnections = 0;
+ // State variables
+ private boolean initialized = false;
+ private Stack available = new Stack();
+ private Stack used = new Stack();
+ private Object lock = new Object()
+ ;
+ private PGConnectionPoolDataSource source;
+
+ /**
+ * Gets a description of this DataSource.
+ */
+ public String getDescription()
+ {
+ return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.Driver.getVersion();
+ }
+
+ /**
+ * Ensures the DataSource properties are not changed after the DataSource has
+ * been used.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Server Name cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setServerName(String serverName)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ super.setServerName(serverName);
+ }
+
+ /**
+ * Ensures the DataSource properties are not changed after the DataSource has
+ * been used.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Database Name cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setDatabaseName(String databaseName)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ super.setDatabaseName(databaseName);
+ }
+
+ /**
+ * Ensures the DataSource properties are not changed after the DataSource has
+ * been used.
+ *
+ * @throws java.lang.IllegalStateException
+ * The User cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setUser(String user)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ super.setUser(user);
+ }
+
+ /**
+ * Ensures the DataSource properties are not changed after the DataSource has
+ * been used.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Password cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setPassword(String password)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ super.setPassword(password);
+ }
+
+ /**
+ * Ensures the DataSource properties are not changed after the DataSource has
+ * been used.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Port Number cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setPortNumber(int portNumber)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ super.setPortNumber(portNumber);
+ }
+
+ /**
+ * Gets the number of connections that will be created when this DataSource
+ * is initialized. If you do not call initialize explicitly, it will be
+ * initialized the first time a connection is drawn from it.
+ */
+ public int getInitialConnections()
+ {
+ return initialConnections;
+ }
+
+ /**
+ * Sets the number of connections that will be created when this DataSource
+ * is initialized. If you do not call initialize explicitly, it will be
+ * initialized the first time a connection is drawn from it.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Initial Connections cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setInitialConnections(int initialConnections)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ this.initialConnections = initialConnections;
+ }
+
+ /**
+ * Gets the maximum number of connections that the pool will allow. If a request
+ * comes in and this many connections are in use, the request will block until a
+ * connection is available. Note that connections for a user other than the
+ * default user will not be pooled and don't count against this limit.
+ *
+ * @return The maximum number of pooled connection allowed, or 0 for no maximum.
+ */
+ public int getMaxConnections()
+ {
+ return maxConnections;
+ }
+
+ /**
+ * Sets the maximum number of connections that the pool will allow. If a request
+ * comes in and this many connections are in use, the request will block until a
+ * connection is available. Note that connections for a user other than the
+ * default user will not be pooled and don't count against this limit.
+ *
+ * @param maxConnections The maximum number of pooled connection to allow, or
+ * 0 for no maximum.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Maximum Connections cannot be changed after the DataSource has been
+ * used.
+ */
+ public void setMaxConnections(int maxConnections)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
+ }
+ this.maxConnections = maxConnections;
+ }
+
+ /**
+ * Gets the name of this DataSource. This uniquely identifies the DataSource.
+ * You cannot use more than one DataSource in the same VM with the same name.
+ */
+ public String getDataSourceName()
+ {
+ return dataSourceName;
+ }
+
+ /**
+ * Sets the name of this DataSource. This is required, and uniquely identifies
+ * the DataSource. You cannot create or use more than one DataSource in the
+ * same VM with the same name.
+ *
+ * @throws java.lang.IllegalStateException
+ * The Data Source Name cannot be changed after the DataSource has been
+ * used.
+ * @throws java.lang.IllegalArgumentException
+ * Another PoolingDataSource with the same dataSourceName already
+ * exists.
+ */
+ public void setDataSourceName(String dataSourceName)
+ {
+ if (initialized)
+ {
+ 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;
+ addDataSource(dataSourceName);
+ }
+ }
+
+ /**
+ * Initializes this DataSource. If the initialConnections is greater than zero,
+ * 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.
+ * @throws java.sql.SQLException
+ * Occurs when the initialConnections is greater than zero, but the
+ * DataSource is not able to create enough physical connections.
+ */
+ public void initialize() throws SQLException
+ {
+ synchronized (lock )
+ {
+ source = createConnectionPool();
+ source.setDatabaseName(getDatabaseName());
+ source.setPassword(getPassword());
+ source.setPortNumber(getPortNumber());
+ source.setServerName(getServerName());
+ source.setUser(getUser());
+ while (available.size() < initialConnections)
+ {
+ available.push(source.getPooledConnection());
+ }
+ initialized = true;
+ }
+ }
+
+ protected boolean isInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Creates the appropriate ConnectionPool to use for this DataSource.
+ */
+ protected PGConnectionPoolDataSource createConnectionPool() {
+ return new PGConnectionPoolDataSource();
+ }
+
+ /**
+ * Gets a <b>non-pooled</b> connection, unless the user and password are the
+ * same as the default values for this connection pool.
+ *
+ * @return A pooled connection.
+ * @throws SQLException
+ * Occurs when no pooled connection is available, and a new physical
+ * connection cannot be created.
+ */
+ public Connection getConnection(String user, String password) throws SQLException
+ {
+ // If this is for the default user/password, use a pooled connection
+ if (user == null ||
+ (user.equals(getUser()) && ((password == null && getPassword() == null) || (password != null && password.equals(getPassword())))))
+ {
+ return getConnection();
+ }
+ // Otherwise, use a non-pooled connection
+ if (!initialized)
+ {
+ initialize();
+ }
+ return super.getConnection(user, password);
+ }
+
+ /**
+ * Gets a connection from the connection pool.
+ *
+ * @return A pooled connection.
+ * @throws SQLException
+ * Occurs when no pooled connection is available, and a new physical
+ * connection cannot be created.
+ */
+ public Connection getConnection() throws SQLException
+ {
+ if (!initialized)
+ {
+ initialize();
+ }
+ return getPooledConnection();
+ }
+
+ /**
+ * Closes this DataSource, and all the pooled connections, whether in use or not.
+ */
+ public void close()
+ {
+ synchronized (lock )
+ {
+ while (available.size() > 0)
+ {
+ PGPooledConnection pci = (PGPooledConnection)available.pop();
+ try
+ {
+ pci.close();
+ }
+ catch (SQLException e)
+ {
+ }
+ }
+ available = null;
+ while (used.size() > 0)
+ {
+ PGPooledConnection pci = (PGPooledConnection)used.pop();
+ pci.removeConnectionEventListener(connectionEventListener);
+ try
+ {
+ pci.close();
+ }
+ catch (SQLException e)
+ {
+ }
+ }
+ used = null;
+ }
+ removeStoredDataSource();
+ }
+
+ protected void removeStoredDataSource() {
+ synchronized (dataSources)
+ {
+ dataSources.remove(dataSourceName);
+ }
+ }
+
+ protected abstract void addDataSource(String 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.
+ */
+ private Connection getPooledConnection() throws SQLException
+ {
+ PooledConnection pc = null;
+ synchronized (lock )
+ {
+ if (available == null)
+ {
+ throw new PSQLException(GT.tr("DataSource has been closed."),
+ PSQLState.CONNECTION_DOES_NOT_EXIST);
+ }
+ while (true)
+ {
+ if (available.size() > 0)
+ {
+ pc = (PooledConnection)available.pop();
+ used.push(pc);
+ break;
+ }
+ if (maxConnections == 0 || used.size() < maxConnections)
+ {
+ pc = source.getPooledConnection();
+ used.push(pc);
+ break;
+ }
+ else
+ {
+ try
+ {
+ // Wake up every second at a minimum
+ lock.wait(1000L);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+ pc.addConnectionEventListener(connectionEventListener);
+ return pc.getConnection();
+ }
+
+ /**
+ * Notified when a pooled connection is closed, or a fatal error occurs
+ * on a pooled connection. This is the only way connections are marked
+ * as unused.
+ */
+ private ConnectionEventListener connectionEventListener = new ConnectionEventListener()
+ {
+ public void connectionClosed(ConnectionEvent event)
+ {
+ ((PooledConnection)event.getSource()).removeConnectionEventListener(this);
+ synchronized (lock )
+ {
+ if (available == null)
+ {
+ return ; // DataSource has been closed
+ }
+ boolean removed = used.remove(event.getSource());
+ if (removed)
+ {
+ available.push(event.getSource());
+ // There's now a new connection available
+ lock.notify();
+ }
+ else
+ {
+ // a connection error occured
+ }
+ }
+ }
+
+ /**
+ * This is only called for fatal errors, where the physical connection is
+ * useless afterward and should be removed from the pool.
+ */
+ public void connectionErrorOccurred(ConnectionEvent event)
+ {
+ ((PooledConnection) event.getSource()).removeConnectionEventListener(this);
+ synchronized (lock )
+ {
+ if (available == null)
+ {
+ return ; // DataSource has been closed
+ }
+ used.remove(event.getSource());
+ // We're now at least 1 connection under the max
+ lock.notify();
+ }
+ }
+ };
+
+ /**
+ * Adds custom properties for this DataSource to the properties defined in
+ * the superclass.
+ */
+ public Reference getReference() throws NamingException
+ {
+ Reference ref = super.getReference();
+ ref.add(new StringRefAddr("dataSourceName", dataSourceName));
+ if (initialConnections > 0)
+ {
+ ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections)));
+ }
+ if (maxConnections > 0)
+ {
+ ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections)));
+ }
+ return ref;
+ }
+}
diff --git a/org/postgresql/ds/jdbc23/AbstractJdbc23SimpleDataSource.java b/org/postgresql/ds/jdbc23/AbstractJdbc23SimpleDataSource.java
new file mode 100644
index 00000000..abae8025
--- /dev/null
+++ b/org/postgresql/ds/jdbc23/AbstractJdbc23SimpleDataSource.java
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+*
+* Copyright (c) 2004-2005, PostgreSQL Global Development Group
+*
+* IDENTIFICATION
+* $PostgreSQL$
+*
+*-------------------------------------------------------------------------
+*/
+package org.postgresql.ds.jdbc23;
+
+import javax.sql.DataSource;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+
+import org.postgresql.ds.common.*;
+
+/**
+ * Simple DataSource which does not perform connection pooling. In order to use
+ * the DataSource, you must set the property databaseName. The settings for
+ * serverName, portNumber, user, and password are optional. Note: these properties
+ * are declared in the superclass.
+ *
+ * @author Aaron Mulder (ammulder@chariotsolutions.com)
+ */
+public class AbstractJdbc23SimpleDataSource extends BaseDataSource implements Serializable
+{
+ /**
+ * Gets a description of this DataSource.
+ */
+ public String getDescription()
+ {
+ return "Non-Pooling DataSource from " + org.postgresql.Driver.getVersion();
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException
+ {
+ writeBaseObject(out);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+ {
+ readBaseObject(in);
+ }
+}
[parent: 3f3bb0f7d7c1]