/*
* 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;
}
}