content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * 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.
   */
  /**
   * (created at 2012-4-17)
   */
  package com.alibaba.cobar.mysql.nio;
  
  import java.util.concurrent.locks.ReentrantLock;
  
  import org.apache.log4j.Logger;
  
  import com.alibaba.cobar.config.Alarms;
  import com.alibaba.cobar.config.model.DataSourceConfig;
  import com.alibaba.cobar.mysql.MySQLDataNode;
  import com.alibaba.cobar.mysql.nio.handler.DelegateResponseHandler;
  import com.alibaba.cobar.mysql.nio.handler.ResponseHandler;
  import com.alibaba.cobar.statistic.SQLRecorder;
  import com.alibaba.cobar.util.TimeUtil;
  
  /**
   * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
   */
  public class MySQLConnectionPool {
      private static final Logger alarm = Logger.getLogger("alarm");
  
      private final MySQLDataNode dataNode;
      private final int index;
      private final String name;
      private final ReentrantLock lock = new ReentrantLock();
      private final MySQLConnectionFactory factory;
      private final DataSourceConfig config;
      private final int size;
  
      private final MySQLConnection[] items;
      private int activeCount;
      private int idleCount;
      private final SQLRecorder sqlRecorder;
  
      public MySQLConnectionPool(MySQLDataNode node, int index, DataSourceConfig config, int size) {
          this.dataNode = node;
          this.size = size;
          this.items = new MySQLConnection[size];
          this.config = config;
          this.name = config.getName();
          this.index = index;
          this.factory = new MySQLConnectionFactory();
          this.sqlRecorder = new SQLRecorder(config.getSqlRecordCount());
      }
  
      public int getIndex() {
          return index;
      }
  
      public String getName() {
          return name;
      }
  
      public void getConnection(final ResponseHandler handler, final Object attachment) throws Exception {
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              // too many active connections
              if (activeCount >= size) {
                  StringBuilder s = new StringBuilder();
                  s.append(Alarms.DEFAULT).append("[name=").append(name).append(",active=");
                  s.append(activeCount).append(",size=").append(size).append(']');
                  alarm.error(s.toString());
              }
  
              // get connection from pool
              final MySQLConnection[] items = this.items;
              for (int i = 0, len = items.length; idleCount > 0 && i < len; ++i) {
                  if (items[i] != null) {
                      MySQLConnection conn = items[i];
                      items[i] = null;
                      --idleCount;
                      if (conn.isClosedOrQuit()) {
                          continue;
                      } else {
                          ++activeCount;
                          conn.setAttachment(attachment);
                          handler.connectionAcquired(conn);
                          return;
                      }
                  }
              }
  
              ++activeCount;
          } finally {
              lock.unlock();
          }
  
          // create connection
          factory.make(this, new DelegateResponseHandler(handler) {
              private boolean deactived;
  
              @Override
              public void connectionError(Throwable e, MySQLConnection conn) {
                  lock.lock();
                  try {
                      if (!deactived) {
                          --activeCount;
                          deactived = true;
                      }
                  } finally {
                      lock.unlock();
                  }
                  handler.connectionError(e, conn);
              }
  
              @Override
              public void connectionAcquired(MySQLConnection conn) {
                  conn.setAttachment(attachment);
                  handler.connectionAcquired(conn);
              }
          });
      }
  
      public void releaseChannel(MySQLConnection c) {
          if (c == null || c.isClosedOrQuit()) {
              return;
          }
  
          // release connection
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              final MySQLConnection[] items = this.items;
              for (int i = 0; i < items.length; i++) {
                  if (items[i] == null) {
                      ++idleCount;
                      --activeCount;
                      c.setLastTime(TimeUtil.currentTimeMillis());
                      items[i] = c;
                      return;
                  }
              }
          } finally {
              lock.unlock();
          }
  
          // close excess connection
          c.quit();
      }
  
      public void deActive() {
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              --activeCount;
          } finally {
              lock.unlock();
          }
      }
  
      public SQLRecorder getSqlRecorder() {
          return sqlRecorder;
      }
  
      public DataSourceConfig getConfig() {
          return config;
      }
  
  }
  