package org.opensource.clearpool.core;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.PooledConnection;
import org.opensource.clearpool.configuration.ConfigurationVO;
import org.opensource.clearpool.console.MBeanFacade;
import org.opensource.clearpool.exception.ConnectionPoolException;
import org.opensource.clearpool.exception.ConnectionPoolStateException;
import org.opensource.clearpool.logging.PoolLogger;
import org.opensource.clearpool.logging.PoolLoggerFactory;
/**
* The pool provide two kind of database connection pool.Please check {@link CommonPoolContainer} if
* you want the details.
*
* The pool have 3 different states here.We can do nothing if the pool is unInitialized or
* destroyed, and we can do everything if pool is initialized.
*
* Note:this class is a singleton class.The reason that we don't use ENUM singleton model which is
* recommend by Joshua Bloch is because ENUM instance cann't be released.
*
* @author xionghui
* @date 26.07.2014
* @version 1.0
*/
class ConnectionPoolImpl implements IConnectionPool {
private static final PoolLogger LOGGER = PoolLoggerFactory.getLogger(ConnectionPoolImpl.class);
// the INSTANCE should be the front of the SINGLETON_MARK
private static ConnectionPoolImpl instance = new ConnectionPoolImpl();
// the SINGLETON_MARK make sure the ClearPool is a singleton
private final static boolean SINGLETON_MARK;
static {
SINGLETON_MARK = true;
}
/**
* we get 3 states here.
*
* state=0:unInitialized; state=1:initialized; state=2:destroyed.
*
*/
private volatile int state = 0;
// it is used to handle the pool.
static volatile ConnectionPoolContainer poolContainer;
/**
* Hide the constructor
*/
private ConnectionPoolImpl() {
// whenever we invoke the constructor by reflection,we throw a
// ConnectionPoolException.
if (SINGLETON_MARK) {
throw new ConnectionPoolException("create ClearPool illegal");
}
}
/**
* Get a instance of connection pool
*/
static ConnectionPoolImpl getInstance() {
ConnectionPoolImpl tempInstance = instance;
if (tempInstance == null) {
throw new ConnectionPoolStateException("clearpool had been destroyed");
}
return tempInstance;
}
/**
* Init pool by the default path
*/
@Override
public void init() {
this.initPath(null);
}
/**
* Init pool by the given path
*/
@Override
public void initPath(String path) {
this.load(path, null);
}
/**
* Init pool by vo
*/
@Override
public void initVO(ConfigurationVO vo) {
Map<String, ConfigurationVO> cfgMap = new HashMap<String, ConfigurationVO>();
vo.init();
cfgMap.put(vo.getAlias(), vo);
this.load(null, cfgMap);
}
/**
* Init pool by voList
*/
@Override
public void initVOList(List<ConfigurationVO> voList) {
Map<String, ConfigurationVO> cfgMap = new HashMap<String, ConfigurationVO>();
for (ConfigurationVO vo : voList) {
vo.init();
if (cfgMap.put(vo.getAlias(), vo) != null) {
throw new ConnectionPoolStateException(
"ConfigurationVOs' alias " + vo.getAlias() + " repeat");
}
}
this.load(null, cfgMap);
}
/**
* Init pool by path or cfgMap.
*
* Note:one of path and cfgMap is null.
*/
private void load(String path, Map<String, ConfigurationVO> cfgMap) {
long begin = System.currentTimeMillis();
// load cfg to init pool
ConnectionPoolContainer container = ConnectionPoolContainer.load(path, cfgMap);
if (container != null) {
poolContainer = container;
LOGGER.info(
"connection pool initialized.it cost " + (System.currentTimeMillis() - begin) + "ms");
}
this.checkDestroyed();
state = 1;
}
public PooledConnection getPooledConnection() throws SQLException {
this.checkDestroyed();
return poolContainer.getConnection();
}
@Override
public PooledConnection getPooledConnection(String name) throws SQLException {
this.checkDestroyed();
return poolContainer.getConnection(name);
}
@Override
public Connection getConnection() throws SQLException {
PooledConnection pooledCon = this.getPooledConnection();
if (pooledCon == null) {
return null;
}
return pooledCon.getConnection();
}
@Override
public Connection getConnection(String name) throws SQLException {
PooledConnection pooledCon = this.getPooledConnection(name);
if (pooledCon == null) {
return null;
}
return pooledCon.getConnection();
}
@Override
public void close(String name) {
this.checkDestroyed();
ConnectionPoolContainer tempContainer = poolContainer;
if (tempContainer == null) {
return;
}
tempContainer.remove(name);
LOGGER.info("remove pool " + name);
}
@Override
public void close() {
this.checkDestroyed();
// remove all the pool
this.removeAll();
LOGGER.info("the pool is removed");
}
/**
* Remove all the pool.
*/
private void removeAll() {
ConnectionPoolContainer tempContainer = poolContainer;
if (tempContainer == null) {
return;
}
tempContainer.remove();
}
@Override
public void destory() {
if (state == 2) {
return;
}
/**
* When we destroy the pool,we should destroy this singleton too,otherwise it will cause a
* memory leak.
*/
instance = null;
state = 2;
ConnectionPoolContainer.destoryHooks();
// remove all the pool
this.removeAll();
MBeanFacade.stop();
poolContainer = null;
LOGGER.info("the pool is destroyed");
}
/**
* Check the state if it's destroyed.
*/
private void checkDestroyed() {
if (state == 2) {
throw new ConnectionPoolStateException("clearpool have been destroyed");
}
}
}