package jef.database.innerpool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;
import jef.common.log.LogUtil;
import jef.common.pool.PoolStatus;
import jef.database.DbCfg;
import jef.database.DbUtils;
import jef.database.datasource.AbstractDataSource;
import jef.database.datasource.DataSourceInfo;
import jef.database.datasource.SimpleDataSource;
import jef.database.innerpool.PoolService.CheckableConnection;
import jef.tools.JefConfiguration;
/**
* 最简单的连接池实现。
*
* 直接封装为J2EE标准的DataSource的实现。 目前SimplePool已经继承了{@link javax.sql.DataSource}
* 该连接池的功能 1、在指定的大小范围内自动管理连接数 2、在每次拿取连接时检查连接的可用性,如果不可用会自动重连。
*
* @author jiyi
*
*/
public final class SimplePooledDatasource extends AbstractDataSource implements IPool<Connection>, CheckablePool,ConnectionPoolDataSource {
private DataSource datasource;
private int max;
private int min;
private int validationTimeout;
private String testSQL;
private BlockingQueue<Connection> freeConns;
private AtomicInteger used = new AtomicInteger();// 被取走的连接数
/**
* 空构造器
*/
public SimplePooledDatasource() {
this(JefConfiguration.getInt(DbCfg.DB_CONNECTION_POOL, 3), JefConfiguration.getInt(DbCfg.DB_CONNECTION_POOL_MAX, 50), new SimpleDataSource());
}
/**
* 带参数构造
*
* @param min
* 池最小
* @param max
* 池最大
* @param ds
* datasource
*/
public SimplePooledDatasource(int min, int max, DataSource ds) {
if (min > max)
min = max;
this.min = min;
this.max = max;
this.datasource = ds;
freeConns = new LinkedBlockingQueue<Connection>(max);
PoolReleaseThread.getInstance().addPool(this);
PoolCheckThread.getInstance().addPool(this);
}
public void setDatasource(DataSource datasource) {
checkNotUsed();
this.datasource = datasource;
}
public void setMax(int max) {
checkNotUsed();
this.max = max;
this.freeConns = new LinkedBlockingQueue<Connection>(max);
}
public void setMin(int min) {
checkNotUsed();
this.min = min;
}
public Connection poll() throws SQLException {
try {
Connection conn=freeConns.poll();
if(conn!=null){
used.incrementAndGet();
return conn;
}
if (used.get() < max) {// 尝试用新连接
used.incrementAndGet();// 必须立刻累加计数器,否则并发的线程会立刻抢先创建对象,从而超出连接池限制
conn = datasource.getConnection();
} else {
used.incrementAndGet(); // 提前计数,并发下为了严格阻止连接池超出上限,必须这样做
conn = freeConns.poll(5000000000L, TimeUnit.NANOSECONDS);// 5秒
if (conn == null) {
used.decrementAndGet();// 回滚计数器
throw new SQLException("No connection avaliable now." + getStatus());
}
conn = ensureOpen(conn);
}
return conn;
} catch (InterruptedException e) {
throw new SQLException(e);
}
}
// 检查并确保连接可用
private Connection ensureOpen(Connection conn) throws SQLException {
boolean closed = true;
try {
closed = conn.isClosed(); //不准确的判断
} catch (SQLException e) {
DbUtils.closeConnection(conn);
}
if (closed) {
return datasource.getConnection();
} else {
return conn;
}
}
public void offer(Connection conn) {
boolean success = freeConns.offer(conn);
if (!success) {
DbUtils.closeConnection(conn);// 塞不下了。肯定是关闭掉
}
used.decrementAndGet();
}
public PoolStatus getStatus() {
int used = this.used.get();
int free = freeConns.size();
return new PoolStatus(max, min, used + free, used, free);
}
public DataSource getDatasource() {
return datasource;
}
public void closeConnectionTillMin() {
if (freeConns.size() > min) {
Connection conn;
// 注意下面两个条件顺序必须确保poll操作在后,因为poll操作会变更集合的Size
while (freeConns.size() > min && (conn = freeConns.poll()) != null) {
DbUtils.closeConnection(conn);
}
}
}
public void close() throws SQLException {
max = 0;
min = 0;
closeConnectionTillMin();
PoolReleaseThread.getInstance().removePool(this);
}
@Override
protected Class<? extends DataSource> getWrappedClass() {
return null;
}
/*
* 如果当前连接池不是出于初始状态,那么抛出异常
*/
private void checkNotUsed() {
if (used.get() > 0 || (freeConns != null && !freeConns.isEmpty())) {
throw new IllegalStateException("Current connection pool is in using. please set arguments before use it.");
}
}
// /////////////////////////JDBC的连接池实现(不可重入)/////////////////////////////////
public Connection getConnection() throws SQLException {
return new PooledImpl(poll());
}
public Connection getConnection(String username, String password) throws SQLException {
return (Connection) getPooledConnection(username, password);
}
private final class PooledImpl extends AbstractJDBCConnection implements PooledConnection,CheckableConnection {
PooledImpl(Connection conn) {
this.conn = conn;
}
public void close() throws SQLException {
SimplePooledDatasource.this.offer(this.conn);
}
public Connection getConnection() throws SQLException {
return conn;
}
public void addConnectionEventListener(ConnectionEventListener listener) {
}
public void removeConnectionEventListener(ConnectionEventListener listener) {
}
public void addStatementEventListener(StatementEventListener listener) {
}
public void removeStatementEventListener(StatementEventListener listener) {
}
public boolean isUsed() {
return false;
}
public void setInvalid() {
DbUtils.closeConnection(conn);
conn=null;
}
public boolean checkValid(String testSql) throws SQLException {
if(conn==null)return true;
PreparedStatement st=conn.prepareStatement(testSql);
try{
st.execute();
return true;
}finally{
DbUtils.close(st);
}
}
public boolean checkValid(int timeout) throws SQLException {
if(conn==null)return true;
return conn.isValid(timeout);
}
}
public PooledConnection getPooledConnection() throws SQLException {
return new PooledImpl(poll());
}
public PooledConnection getPooledConnection(String username, String password) throws SQLException {
try {
Connection conn;
if (freeConns.isEmpty() && used.get() < max) {// 尝试用新连接
conn = datasource.getConnection(username, password);
} else {
conn = freeConns.poll(5000000000L, TimeUnit.NANOSECONDS);// 5秒
if (conn == null) {
throw new SQLException("No connection avaliable now." + getStatus());
}
conn = ensureOpen(conn);
}
used.incrementAndGet();
return new PooledImpl(conn);
} catch (InterruptedException e) {
throw new SQLException(e);
}
}
public String getUrl() {
return getDsi().getUrl();
}
public void setUrl(String url) {
getDsi().setUrl(url);
}
public String getUsername() {
return getDsi().getUser();
}
public void setUsername(String user) {
getDsi().setUser(user);
}
public String getPassword() {
return getDsi().getPassword();
}
public void setPassword(String password) {
getDsi().setPassword(password);
}
public String getDriverClass() {
return getDsi().getDriverClass();
}
public void setDriverClass(String driverClass) {
getDsi().setDriverClass(driverClass);
}
/**
* 获得验证超时时间
*
* @return
*/
public int getValidationTimeout() {
return validationTimeout;
}
/**
* 设置验证超时时间
*
* @param validationTimeout
*/
public void setValidationTimeout(int validationTimeout) {
this.validationTimeout = validationTimeout;
}
private DataSourceInfo getDsi() {
if (datasource instanceof DataSourceInfo) {
return (DataSourceInfo) datasource;
} else if (datasource == null) {
SimpleDataSource sds = new SimpleDataSource();
datasource = sds;
return sds;
} else {
throw new UnsupportedOperationException("The datasource " + datasource.getClass() + " Doesn't support.");
}
}
public String getTestSQL() {
return testSQL;
}
public void setTestSQL(String string) {
this.testSQL=string;
}
private class I implements Iterator<PooledImpl>{
private Iterator<Connection> raw;
I(Iterator<Connection> iterator) {
this.raw=iterator;
}
public boolean hasNext() {
return raw.hasNext();
}
public PooledImpl next() {
return new PooledImpl(raw.next());
}
public void remove() {
}
}
public void doCheck() {
if(freeConns==null)return;
int total=freeConns.size();
Iterator<PooledImpl> iter=new I(freeConns.iterator());
int invalid=PoolService.doCheck(this.testSQL, iter);
LogUtil.debug("Checked [{}]. total:{}, invalid:{}", this, total, invalid);
}
}