poolingdatasource.java
来自「PostgreSQL7.4.6 for Linux」· Java 代码 · 共 495 行
JAVA
495 行
package org.postgresql.jdbc2.optional;import javax.sql.*;import javax.naming.*;import java.util.*;import java.sql.Connection;import java.sql.SQLException;/** * 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) * @version $Revision: 1.3 $ */public class PoolingDataSource extends BaseDataSource implements DataSource{ private static Map dataSources = new HashMap(); static PoolingDataSource getDataSource(String name) { return (PoolingDataSource)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 ConnectionPool 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; dataSources.put(dataSourceName, this); } } /** * 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 ConnectionPool createConnectionPool() { return new ConnectionPool(); } /** * 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) { PooledConnectionImpl pci = (PooledConnectionImpl)available.pop(); try { pci.close(); } catch (SQLException e) {} } available = null; while (used.size() > 0) { PooledConnectionImpl pci = (PooledConnectionImpl)used.pop(); pci.removeConnectionEventListener(connectionEventListener); try { pci.close(); } catch (SQLException e) {} } used = null; } 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. */ private Connection getPooledConnection() throws SQLException { PooledConnection pc = null; synchronized (lock) { if (available == null) { throw new SQLException("DataSource has been closed."); } 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; }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?