티스토리 뷰

Programming/Java

ConnectionPool

swhwang 2016. 5. 4. 23:45



import java.sql.DriverManager;

import java.sql.Driver;

import java.sql.Connection;

import java.sql.SQLException;

import java.util.List;

import java.util.LinkedList;

import java.util.Map;

import java.util.HashMap;

import java.util.Iterator;



/**

 * This class defines a pooling mechanism for JDBC Connection objects.

 *

 * <P>

 * This class provides the user with a great deal of flexibility.

 * The user can:

 * <UL>

 *   <LI>specify the [min,max] range of number of connections in the pool

 *   <LI>acquire connections with three mechanisms: (a) no wait (throws

 *       a {@link ConnNotAvailException} if no connection is immediately available),

 *       (see {@link #getConnectionNoWait()})

 *       (b) wait indefinitely (at least until the pool is shutdown), or

 *       (see {@link #getConnectionWait()})

 *       (c) wait for a specified amount of time

 *       (see {@link #getConnectionWait(int)})

 *   <LI>specify a "courtesy shutdown wait period"

 *       (see {@link MaintenanceRunner#shutdownCourtesyTimeout})

 * </UL>

 * </P>

 *

 * <P>

 * This class uses a "closed synchronization" idiom.  All of the non-trivial

 * public methods are synchronized and none of the private methods are.

 * </P>

 *

 * <P>Revision History:

 * <UL>

 *   <LI>v1.4 [21-Jan-2001, BB] Fixed the {@link MaintenanceRunner} behavior

 *       to keep the minimum number of connections in the free list.

 *   <LI>v1.3 [20-Jan-2001, BB] Dropped the singular <TT>getConnection</TT>

 *       method, but promoted the {@link #getConnectionNoWait()} and the

 *       {@link #getConnectionWait()} methods to public access and added the

 *       {@link #getConnectionWait(int)} method to round out the functionality.

 *   <LI>v1.2 [19-Jan-2001, BB] Implemented the {@link MaintenanceRunner}

 *       inner class and {@link #shutdown()} method and fixed several bugs

 *   <LI>v1.1 [Nov-2000, BB] Scraped most of the old code as it used old

 *       Collections classes and had a poorly designed "maintenance" mechanism.

 *   <LI>v1.0 [Oct-2000, BB] First cut; based on code from the SL-310 course

 *       from Sun Educational Services.

 * </UL>

 *

 * @author Bryan Basham <b_basham@yahoo.com>

 * @version 1.4  21-Jan-2001

 */

public class ConnectionPool {


  /**

   * This constant defines the default minimum number of connections.

   */

  private static final int DEFAULT_MIN_CONN = 5;


  /**

   * This constant defines the default maximum number of connections.

   */

  private static final int DEFAULT_MAX_CONN = 20;


  //

  // basic data attributes (properties)

  //


  /**

   * This read-only property records the fully-qualified JDBC Driver class.

   */

  private String jdbcDriverClass;


  /**

   * This read-only property records the database connection URL for this pool.

   */

  private String jdbcURL;


  /**

   * This read-only property records the database connection user-name for this pool.

   */

  private String jdbcUserName;


  /**

   * This read-only property records the database connection password for this pool.

   */

  private String jdbcPassword;


  /**

   * This read-only property records the minimum number of connections in this pool.

   */

  private int minimumConnections;


  /**

   * This read-only property records the maximum number of connections allowed in this pool.

   */

  private int maximumConnections;


  //

  // internal implementation attributes

  //


  /**

   * This stores the available database connections.

   */

  private transient List freeConnections = new LinkedList();


  /**

   * This stores the connections currently being used by the client.

   * The mapping is Connection-&LT;Long, where the Long is start time

   * that the connection was allocated to the client.

   */

  private transient Map usedConnections = new HashMap();


  /**

   * This Boolean attribute is set when the {@link #shutdown()} method is called.

   * It is used to tell this object that no new connections should be granted.

   * It also tells the {@link MaintenanceRunner} thread to begin the shutdown

   * process.

   */

  private transient boolean shuttingDown = false;


  /**

   * This Boolean attribute is set when the {@link MaintenanceRunner} thread

   * has completed the shutdown process.

   */

  private transient boolean shutdownComplete = false;


  /**

   * This holds the thread running the MaintenanceRunner object.

   */

  private transient Thread maintThread = null;


  //

  // Constructors

  //


  /**

   * Constructor for the ConnectionPool.

   * <P>

   *   It is up to the client using the ConnectionPool to register the

   *   necessary JDBC drivers before using this class.

   * </P>

   *

   * @param jdbcDriverClass  The JDBC fully-qualified Driver class name

   * @param jdbcURL  The JDBC connect string. e.g. 'jdbc:pointbase:SL314'

   * @param jdbcUserName  Database login name. e.g. 'Scott'

   * @param jdbcPassword  Database password. e.g. 'Tiger'

   * @param minimumConnections  Minimum number of connections to start with.

   * @param maximumConnections  Maximum number of connections in dynamic pool.

   * @param shutdownCourtesyTimeout  The "courtesy shutdown wait period"

   *

   * @exception ClassNotFoundException  Thrown if the jdbcDriverClass is not

   *    found in the CLASSPATH

   * @exception InstantiationException  Thrown if the jdbcDriverClass is not

   *    a concrete class

   * @exception IllegalAccessException  Thrown if the jdbcDriverClass or its

   *    initializer is not accessible

   * @exception SQLException  Thrown on a JDBC exception

   */

  public ConnectionPool(String jdbcDriverClass, String jdbcURL,

                        String jdbcUserName, String jdbcPassword,

                        int minimumConnections, int maximumConnections,

                        int shutdownCourtesyTimeout)

    throws ClassNotFoundException, InstantiationException,

           IllegalAccessException, SQLException {


//System.out.println("[CP] in constructor");


    // Initialize basic attributes

    this.jdbcDriverClass    = jdbcDriverClass;

    this.jdbcURL            = jdbcURL;

    this.jdbcUserName       = jdbcUserName;

    this.jdbcPassword       = jdbcPassword;

    this.minimumConnections = minimumConnections;

    this.maximumConnections = maximumConnections;


    // Register the JDBC driver with the DriveManager

    Driver driver = (Driver) Class.forName(jdbcDriverClass).newInstance();

    DriverManager.registerDriver(driver);


    // Initialize implementation attributes

    for (int i=0; i < minimumConnections; i++) {

      freeConnections.add(makeConnection());

    }


    // Initialize the CP maintenance thread

    maintThread = new Thread(new MaintenanceRunner(shutdownCourtesyTimeout));

    maintThread.start();

  }


  /**

   * Constructor for the ConnectionPool.

   * <P>

   *   I tis up to the clien tusing the ConnectionPool to register the

   *   necessary JDBC drivers before using this class.

   * </P>

   *

   * @param jdbcDriverClass JDBC fully-qualified Driver class name

   * @param jdbcURL tJDBC connec tstring. e.g. 'jdbc:pointbase:SL314'

   * @param jdbcUserName    Database login name. e.g. 'Scott'

   * @param jdbcPassword    Database password. e.g. 'Tiger'

   * @param minimumConnections  Minimum number of connections to star twith.

   * @param maximumConnections  Maximum number of connections in dynamic pool.

   *

   * @exception ClassNotFoundException  Thrown if the jdbcDriverClass is not

   *    found in the CLASSPATH

   * @exception InstantiationException  Thrown if the jdbcDriverClass is not

   *    a concrete class

   * @exception IllegalAccessException  Thrown if the jdbcDriverClass or its

   *    initializer is no taccessible

   * @exception SQLException  Thrown on a JDBC exception

   */

  public ConnectionPool(String jdbcDriverClass, String jdbcURL,

                        String jdbcUserName, String jdbcPassword,

                        int minimumConnections, int maximumConnections)

    throws ClassNotFoundException, InstantiationException,

           IllegalAccessException, SQLException {

    this(jdbcDriverClass, jdbcURL, jdbcUserName, jdbcPassword,

         minimumConnections, maximumConnections,

         MaintenanceRunner.DEFAULT_SHUTDOWN_TIMEOUT);

  }


  /**

   * Constructor for the ConnectionPool.

   * <P>

   *   I tis up to the clien tusing the ConnectionPool to register the

   *   necessary JDBC drivers before using this class.

   * </P>

   *

   * @param jdbcDriverClass JDBC fully-qualified Driver class name

   * @param jdbcURL tJDBC connec tstring. e.g. 'jdbc:pointbase:SL314'

   * @param jdbcUserName    Database login name. e.g. 'Scott'

   * @param jdbcPassword    Database password. e.g. 'Tiger'

   *

   * @exception ClassNotFoundException  Thrown if the jdbcDriverClass is not

   *    found in the CLASSPATH

   * @exception InstantiationException  Thrown if the jdbcDriverClass is not

   *    a concrete class

   * @exception IllegalAccessException  Thrown if the jdbcDriverClass or its

   *    initializer is no taccessible

   * @exception SQLException  Thrown on a JDBC exception

   */

  public ConnectionPool(String jdbcDriverClass, String jdbcURL,

                        String jdbcUserName, String jdbcPassword)

    throws ClassNotFoundException, InstantiationException,

           IllegalAccessException, SQLException {

    this(jdbcDriverClass, jdbcURL, jdbcUserName, jdbcPassword,

         DEFAULT_MAX_CONN, DEFAULT_MIN_CONN,

         MaintenanceRunner.DEFAULT_SHUTDOWN_TIMEOUT);

  }


  //

  // Accessors for basic properties

  //


  /**

   * This accessor returns the JDBC URL for this connection pool.

   */

  public String getJdbcDriverClass() {

    return jdbcDriverClass;

  }


  /**

   * This accessor returns the JDBC URL for this connection pool.

   */

  public String getJdbcURL() {

    return jdbcURL;

  }


  /**

   * This accessor returns the JDBC user-name for this connection pool.

   */

  public String getJdbcUserName() {

    return jdbcUserName;

  }


  /**

   * This accessor returns the JDBC password for this connection pool.

   */

  public String getJdbcPassword() {

    return jdbcPassword;

  }


  /**

   * This accessor returns the minimum number of connections for this pool.

   */

  public int getMinimumConnections() {

    return minimumConnections;

  }


  /**

   * This accessor returns the maximum number of connections for this pool.

   */

  public int getMaximumConnections() {

    return maximumConnections;

  }


  //

  // Public methods

  //


  /**

   * This method immediately returns the nex tavailable connection.

   * I twill never wait for a free connection.  Instead, i twill

   * throw a ConnNotAvailException if no connection is immediately available.

   *t

   * @return Connection the nex tavailable JDBC connection

   * @exception ConnNotAvailException  Thrown if no connection is available

   * @exception ShuttingDownException  Thrown if the connection pool has been ordered to shutdown

   */

  public synchronized Connection getConnectionNoWait()

         throws ConnNotAvailException, ShuttingDownException {

    Connection result = null;


    // If the pool is being shutdown, throw an exception

    if ( shuttingDown ) {

      // Otherwise, throw a ShuttingDownException

      throw new ShuttingDownException();

    }


    // Attempt to ge ta connection

    result = getConnectionInternal();


    // Throw an exception if no connection is available

    if ( result == null ) {

      throw new ConnNotAvailException();

    }

//System.out.println("[CP] go ta connection (" + resul t+ ")");


    // Return it

    return result;

  }


  /**

   * This method returns the nex tavailable connection.

   * <P> I twill wait indefinitely for a free connection, unless the

   *     {@link #shuttingDown} flag is set.

   * <P> Note: this method is always called within a synchronized public method.

   * @return Connection the nex tavailable JDBC connection

   * @exception ShuttingDownException  Thrown if the connection pool has been ordered to shutdown

   */

  public synchronized Connection getConnectionWait()

         throws ShuttingDownException {

    Connection result = null;


    // wait until a free connection is available (or until the pool

    // is being shu tdown).

    while (    (! shuttingDown)

               && ((result = getConnectionInternal()) == null) ) {

//System.out.println("[CP] waiting for a connection");

      try { this.wait(); } catch (InterruptedException ex) { }

    }


    // If the pool is being shutdown, throw an exception

    if ( shuttingDown ) {

      // Otherwise, throw a ShuttingDownException

      throw new ShuttingDownException();

    }


//System.out.println("[CP] finally go ta connection (" + resul t+ ")");


    // Return it

    return result;

  }


  /**

   * This method returns the nex tavailable connection and will wait

   * a specified amoun tof time to ge tone.

   *

   * @return Connection the nex tavailable JDBC connection

   * @exception ConnNotAvailException  Thrown if no connection is available

   * @exception ShuttingDownException  Thrown if the connection pool has been ordered to shutdown

   */

  public synchronized Connection getConnectionWait(int timeout)

         throws ShuttingDownException, ConnNotAvailException {

    Connection result = null;


    // If the pool is being shutdown, throw an exception

    if ( shuttingDown ) {

      throw new ShuttingDownException();

    }


    // Attempt to ge ta connection.

    result = getConnectionInternal();

    if ( result != null ) {

//System.out.println("[CP] go ta connection (" + resul t+ ") immediately");

      return result;

    }


    // If no tfree connection is ye tavailable,

    // then wait until a free connection is available

    try { this.wait(timeout); } catch (InterruptedException ex) { }


    // Check again if the pool is being shutdown

    if ( shuttingDown ) {

      throw new ShuttingDownException();

    }


    // Attempt to ge ta connection one last time

    result = getConnectionInternal();

    if ( result == null ) {

      // Throw an exception if no connection is available

      throw new ConnNotAvailException();

    } else {

      // Otherwise, return it

//System.out.println("[CP] go ta connection (" + resul t+ ") after waiting");

      return result;

    }

  }


  /**

   * Frees a connection.t

   */

  public synchronized void releaseConnection(Connection connection)

    throws WrongPoolException, SQLException {

    if ( usedConnections.containsKey(connection) ) {

      usedConnections.remove(connection);

      if ( ! connection.isClosed() ) {

        if ( shuttingDown ) {

//System.out.println("[CP] releasing connection during shutdown " + connection);

          try {

            connection.close();

          } catch (SQLException sex) { }

        } else {

//System.out.println("[CP] releasing connection " + connection);

          freeConnections.add(connection);

        }

      } else {

//System.out.println("[CP] releasing closed connection " + connection);

        // do nothing, let the connection object be GCed

      }

    } else {

      throw new WrongPoolException();

    }

    this.notifyAll();

  }


  /**

   * This method tells the connection pool to shutdown.

   * This method signals the {@link MaintenanceRunner} thread to perform

   * the shutdown operations.  The completion of tha tprocess is signaled

   * by the {@link #shutdownComplete} attribute.

   */

  public synchronized void shutdown() {

//System.out.println("[CP] signaling shutdown");

    shuttingDown = true;

    this.notifyAll();

    while ( ! shutdownComplete ) {

      try { this.wait(); } catch (InterruptedException ex) { }

    }

//System.out.println("[CP] shutdown complete");

  }


  /**

   * Returns the age of a connection.

   * @return long the time (in milliseconds) since i twas handed out

   * to an application (-1 if no tbeing used)

   */

  public synchronized long getAge(Connection connection) {

    Long start_time = (Long) usedConnections.get(connection);

    if ( start_time == null ) {

      return -1;

    } else {

      return ( System.currentTimeMillis() - start_time.longValue() );

    }

  }


  /**

   * Returns the number of connections in the dynamic pool.t

   */

  public synchronized int getSize() {

    return usedConnections.size() + freeConnections.size();

  }


  /**

   * Returns the number of connections in use.t

   */

  public synchronized int getUseCount() {

    return usedConnections.size();

  }


  //

  // Private methods

  //


  /**

   * This method returns the nex tavailable (or new) connection.

   * <P> Note: this method is always called within a synchronized public method.

   * @return Connection the nex tavailable JDBC connection (or <TT>null</TT> if no connection is available)

   */

  private Connection getConnectionInternal() {

    Connection result = null;


    if ( ! freeConnections.isEmpty() ) {

      // get the firs titem on the free list

      result = (Connection) freeConnections.remove(0);

//System.out.println("[CP] getting free connection " + result);

    } else if ( usedConnections.size() < maximumConnections ) {

      // try to make a new connection

      try {

        result = makeConnection();

//System.out.println("[CP] getting new connection " + result);

      } catch (SQLException sex) {

        sex.printStackTrace(System.err);

      }

    }


    // Store i tin used connections map

    if ( result != null ) {

      // get the time of this request

      Long start_time = new Long(System.currentTimeMillis());

      // pu tthis connection in the used map

      usedConnections.put(result, start_time);

    }


    // Return it

    return result;

  }


  /**

   * This method creates a new JDBC connection for this pool.

   * @return Connection a newly created JDBC connection object

   */

  private Connection makeConnection() throws SQLException {

//System.out.println("[CP] making connection " + jdbcURL + "  [" + jdbcUserName + ", " + jdbcPassword + "]");

    if ( jdbcUserName.length() > 0 ) {

      return DriverManager.getConnection(jdbcURL, jdbcUserName, jdbcPassword);

    } else {

      return DriverManager.getConnection(jdbcURL);

    }

  }


  /**

   * This inner class is used to perform clean-up on a connection pool.

   * An object of this class runs in a separate thread (a ta low priority)

   * and closes any extra connections.

   */

  class MaintenanceRunner implements Runnable {


    /**

     * This constan tdefines the defaul tlength of time (10 seconds) that the

     * {@link MaintenanceRunner} thread will wait for used connections to

     * be released.

     */

    private static final int DEFAULT_SHUTDOWN_TIMEOUT = 10 * 1000;


    /**

     * This attribute defines the timeou tpolicy/duration that the shutdown

     * process waits for clients to close "used connections".

     * <UL>

     *  <LI><TT>-1</TT>: don' twait, close all open connections immediately

     *  <LI><TT>0</TT>: wait indefinitely, allow the clients to close connections at their leisure

     *  <LI><TT>&GT;0</TT>: wait for a specified amoun tof time (in milliseconds)

     * </UL>

     */

    private int shutdownCourtesyTimeout;


    /**

     * This constructor allows the client to initialize

     * the {@link #shutdownCourtesyTimeout} value.

     */

    public MaintenanceRunner(int timeout) {

      this.shutdownCourtesyTimeout = timeout;

    }


    /**

     * This constructor initializes the {@link #shutdownCourtesyTimeout}

     * to a defaul t(positive) value.

     */

    public MaintenanceRunner() {

      this(DEFAULT_SHUTDOWN_TIMEOUT);

    }


    /**

     * The thread process.  This method synchronizes on the pool object so

     * tha ti tcan perform <TT>wait</TT> operations.  Mos tof the time this

     * thread will be waiting in the {@link #maintLoop()} method.  Once the

     * {@link #shuttingDown} flag is set, then this thread will enter the

     * {@link #shutdown()} process.  This method sets the {@link #shutdownComplete}

     * when i tis done; a twhich time the {@link #shutdown()} method returns and

     * the <TT>run</TT> method return and the thread will halt.

     */

    public void run() {

      synchronized (ConnectionPool.this) {

//System.out.println("[MR] beginning maintenance loop");

        maintLoop();

//System.out.println("[MR] beginning shutdown process");

        shutdown();

//System.out.println("[MR] completed shutdown process");

      }

    }


    /**

     * This method maintains the ideal number of connections in the

     * pool during normal operations (tha tis, before the pool is shutdown).

     */

    private void maintLoop() {

      while ( ! shuttingDown ) {

//System.out.println("[MR] performing maintenance sweep.");


        // Shink the free collection, if there are too many

        for ( int i = getSize();

              // allow the minimum in the free collection a tany time

              freeConnections.size() > minimumConnections;

              i-- ) {

          Connection conn = (Connection) freeConnections.remove(0);

//System.out.println("[MR] removing connection: " + conn);

          try { conn.close(); } catch (SQLException sex) {}

        }


        // Grow the free collection, if there are no tenough

        // This can occur if the clien tprocesses are (errantly) closing

        // the connections before releasing them.

        for ( int i = getSize(); (i < minimumConnections); i++ ) {

          try {

            Connection conn = makeConnection();

            freeConnections.add(conn);

//System.out.println("[MR] creating a new connection " + conn);

          } catch (SQLException sex) {}

        }


        // wait for the next time to perform maintenance

        try { ConnectionPool.this.wait(); } catch (InterruptedException ex) { }

      }

    }


    /**

     * This method implements the "shutting down" process.  It's job is to

     * close the connections in the pool.

     * <P><B>Note:</B>

     * <UL>

     *  <LI>The ConnectionPool object is responsible for rejecting requests

     *      for connections once the {@link #shuttingDown} flag is set.

     *  <LI>This process will allow the clients a "courtesy timeout" if there

     *      are still connections being used.  (see {@link #shutdownCourtesyTimeout})

     * </UL>

     */

    private void shutdown() {

      Iterator connections;


      if ( (shutdownCourtesyTimeout > -1) && ! usedConnections.isEmpty() ) {

        // Play fair: give the client's an opportunity to finish their work

//System.out.println("[MR] waiting for busy connections");

        if ( shutdownCourtesyTimeout == 0 ) {

          // wait indefinitely

          while ( ! usedConnections.isEmpty() ) {

            try {

              ConnectionPool.this.wait(shutdownCourtesyTimeout);

            } catch (InterruptedException ex) { }

          }

        } else {

          // Otherwise, wait for the specified amoun tof time

          if ( ! usedConnections.isEmpty() ) {

            try {

              ConnectionPool.this.wait(shutdownCourtesyTimeout);

            } catch (InterruptedException ex) { }

          }

        }

      }


      // Release free connections

      connections = freeConnections.iterator();

      while ( connections.hasNext() ) {

        Connection conn = (Connection) connections.next();

//System.out.println("[MR] closing free connection " + conn);

        try { conn.close(); } catch (SQLException sex) {}

      }


      // Release used connections

      connections = usedConnections.keySet().iterator();

      while ( connections.hasNext() ) {

        Connection conn = (Connection) connections.next();

//System.out.println("[MR] closing used connection " + conn);

        try { conn.close(); } catch (SQLException sex) {}

      }


      // Repor tback to the thread tha tbegan the shutdown process

      // tha ti thas been completed.

      shutdownComplete = true;

      ConnectionPool.this.notifyAll();

    }

  }

}



'Programming > Java' 카테고리의 다른 글

ObjectNotFoundException  (0) 2016.05.04
MemberVO  (0) 2016.05.04
MemberDAO  (0) 2016.05.04
Manager  (0) 2016.05.04
ConnNotAvailException  (0) 2016.05.04
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함