/**
 * Copyright 2011 - 2013 Xeiam LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.xeiam.yank;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class represents a connection pool. It creates new connections on demand, up to a max number if specified. It also makes sure a connection is still open before it is returned to a client.
 *
 * @author timmolter
 */
public class DBConnectionPool {

  /** slf4J logger wrapper */
  private static Logger logger = LoggerFactory.getLogger(DBConnectionPool.class);

  private int checkedOut = 0;
  private Vector<Connection> pool = new Vector<Connection>();
  private final int maxConn;
  private final String password;
  private final String url;
  private final String user;

  /**
   * Creates new connection pool.
   *
   * @param url The JDBC URL for the database
   * @param user The database user, or null
   * @param password The database user password, or null
   * @param maxConn The maximal number of connections, or 0 for no limit
   */
  public DBConnectionPool(String url, String user, String password, int maxConn) {

    this.url = url;
    this.user = user;
    this.password = password;
    this.maxConn = maxConn;
  }

  /**
   * Checks in a connection to the pool. Notify other Threads that may be waiting for a connection.
   *
   * @param con The connection to check in
   */
  public synchronized void freeConnection(Connection con) {

    if (con != null) {
      try {
        con.rollback();
      } catch (SQLException e) {
        logger.debug("exception while rolling back connection", e);
      }
      // Put the connection at the end of the Vector
      pool.addElement(con);
      checkedOut--;
      notifyAll();
    }
  }

  /**
   * Checks out a connection from the pool. If no free connection is available,
   * a new connection is created unless the max number of
   * connections has been reached. If a free connection has been closed
   * by the database, it is removed from the pool and
   * this method is called again recursively.
   *
   * @return a Connection, null if pool size has been exceeded
   */
  public synchronized Connection getConnection() {

    Connection connection = null;
    if (pool.size() > 0) {
      // Pick the first Connection in the Vector
      // to get round-robin usage
      connection = pool.firstElement();
      pool.removeElementAt(0);
      try {
        if (connection.isClosed()) {
          logger.debug("Removed closed connection from pool");
          // Try again recursively
          connection = getConnection();
        }
      } catch (SQLException e) {
        logger.debug("Removed bad connection from pool", e);
        // Try again recursively
        connection = getConnection();
      }
    }
    else if (maxConn == 0 || checkedOut < maxConn) {
      connection = newConnection();
    }
    if (connection != null) {
      checkedOut++;
    }
    logger.trace("Number of Connections in connection pool = " + checkedOut);
    return connection;
  }

  /**
   * Closes all available connections.
   */
  public synchronized void release() {

    Enumeration<Connection> allConnections = pool.elements();
    while (allConnections.hasMoreElements()) {
      logger.debug("Closing connection...");

      Connection con = allConnections.nextElement();
      try {
        con.close();
        logger.debug("Closed a connection in pool.");
      } catch (SQLException e) {
        logger.error("Couldn't close connection for pool.", e);
      } catch (Exception e) {
        logger.error("Couldn't close connection for pool.", e);
      }
    }
    pool.removeAllElements();
    checkedOut = 0;
  }

  /**
   * Creates a new connection, using a userid and password if specified.
   */
  private Connection newConnection() {

    Connection con = null;
    try {
      if (user == null) {
        con = DriverManager.getConnection(url);
      }
      else {
        con = DriverManager.getConnection(url, user, password);
      }
      logger.debug("Created a new connection in pool.");
    } catch (SQLException e) {
      logger.error("Can't create a new connection for " + url + ".", e);
      return null;
    }
    return con;
  }

  /**
   * For unit testing. Clients should never need this method.
   *
   * @return
   */
  int getCheckedOut() {

    return checkedOut;
  }
}

@@ -726,27 +726,20 @@
 va.util.
-Enumeration
+List
 ;%0Aimport
@@ -1230,22 +1230,20 @@
 private 
-Vector
+List
 %3CConnect
@@ -2316,23 +2316,16 @@
 pool.add
-Element
 (con);%0A 
@@ -2875,16 +2875,52 @@
 ) %3E 0) %7B
+ // if there are some in the pool...
 %0A      /
@@ -3023,66 +3023,61 @@
 ool.
-firstElement();%0A      pool.removeElementAt(0);%0A      try %7B
+remove(0);%0A      try %7B // but first see if it's clean
 %0A   
@@ -3489,16 +3489,48 @@
 xConn) %7B
+ // otherwise the pool was empty
 %0A      c
@@ -3589,24 +3589,66 @@
 n != null) %7B
+ // keep track of how many are checked out
 %0A      check
@@ -3867,113 +3867,39 @@
     
-Enumeration%3CConnection%3E allConnections = pool.elements();%0A    while (allConnections.hasMoreElements()
+for (Connection con : pool
 ) %7B%0A
+%0A
     
@@ -3944,61 +3944,8 @@
 );%0A%0A
-      Connection con = allConnections.nextElement();%0A
     
@@ -4245,25 +4245,13 @@
 ool.
-removeAllElements
+clear
 ();%0A

change pool from Vector to List interface