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]