package org.opensource.clearpool.core; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.sql.PooledConnection; import org.opensource.clearpool.configuration.ConfigurationVO; import org.opensource.clearpool.configuration.XMLConfiguration; import org.opensource.clearpool.console.MBeanFacade; import org.opensource.clearpool.core.hook.CommonHook; import org.opensource.clearpool.core.hook.IdleCheckHook; import org.opensource.clearpool.core.hook.ShutdownHook; import org.opensource.clearpool.exception.ConnectionPoolException; import org.opensource.clearpool.logging.PoolLogger; import org.opensource.clearpool.logging.PoolLoggerFactory; import org.opensource.clearpool.util.PoolLatchUtil; /** * This class is used to parse xml and manage hooks. * * @author xionghui * @date 26.07.2014 * @version 1.0 */ class ConnectionPoolContainer { private static final PoolLogger LOGGER = PoolLoggerFactory.getLogger(ConnectionPoolContainer.class); private static final Lock lock = new ReentrantLock(); private static Thread idleCheckHook; /** * It carry the pool,and it make sure the pool should just be loaded one time. */ private static volatile Map<String, ConnectionPoolManager> poolMap = new HashMap<String, ConnectionPoolManager>(); /** * Load XML and init pool. * * @param path is used to find XML * @return PoolContainer is {@link ConnectionPoolContainer} or {@link UniquePoolContainer} */ static ConnectionPoolContainer load(String path, Map<String, ConfigurationVO> cfgMap) { if (cfgMap == null) { cfgMap = XMLConfiguration.getCfgVO(path); } Set<String> nameSet = cfgMap.keySet(); // check if pool name repeat checkPoolName(nameSet); lock.lock(); try { // double check checkPoolName(nameSet); ConnectionPoolContainer container = ConnectionPoolImpl.poolContainer; // if container is not null,it must be a distributed pool,so we // should fill it instead of create it. if (container == null) { container = new ConnectionPoolContainer(); } container.initPool(cfgMap); // start hooks startHooks(); // wait until hook running. PoolLatchUtil.await(); return container; } finally { lock.unlock(); } } /** * Check if the name of the pool is already loaded. */ private static void checkPoolName(Set<String> nameSet) { if (poolMap.size() == 0) { return; } for (String name : nameSet) { // check if the pool name already init if (poolMap.get(name) != null) { throw new ConnectionPoolException("the pool " + name + " had been loaded"); } } } /** * Init pool chain and start ShutdownHook,IdleGarbageHook. */ private static void startHooks() { Collection<ConnectionPoolManager> poolCollection = poolMap.values(); CommonHook.initPoolChain(poolCollection); if (idleCheckHook == null) { // start MBean MBeanFacade.start(); // register ShutdownHook ShutdownHook.registerHook(); // start IdleCheckHook idleCheckHook = IdleCheckHook.startHook(); } } /** * Interrupt all the daemon hooks. */ static void destoryHooks() { if (idleCheckHook != null) { idleCheckHook.interrupt(); } } /** * Init pool and start MBean if necessary. */ private void initPool(Map<String, ConfigurationVO> cfgMap) { long begin = System.currentTimeMillis(); for (Map.Entry<String, ConfigurationVO> e : cfgMap.entrySet()) { ConfigurationVO cfgVO = e.getValue(); ConnectionPoolManager pool = new ConnectionPoolManager(cfgVO); try { pool.initPool(); } catch (Throwable t) { LOGGER.error("initPool error: ", t); // in case memory reveal. pool.remove(); throw new ConnectionPoolException(t); } String poolName = e.getKey(); poolMap.put(poolName, pool); String alias = cfgVO.getAlias(); String mbeanName = "org.clearpool:type=Pool" + (alias == null ? "" : ",name=" + alias); MBeanFacade.registerMBean(pool, mbeanName, poolName); } long cost = System.currentTimeMillis() - begin; LOGGER.info("initPool cost " + cost + "ms"); } /** * distribute pool don't support {@link #getConnection},we should use * {@link #getConnection(String)} to get connection. */ PooledConnection getConnection() throws SQLException { if (poolMap.size() > 1) { return this.getConnection(null); } PooledConnection pooledConnection = null; for (ConnectionPoolManager pool : poolMap.values()) { // get pool connection pooledConnection = pool.exitPool(); break; } return pooledConnection; } PooledConnection getConnection(String name) throws SQLException { name = (name == null ? null : name.trim()); ConnectionPoolManager pool = poolMap.get(name); if (pool == null) { return null; } // get pool connection PooledConnection pooledConnection = pool.exitPool(); return pooledConnection; } void remove(String name) { name = (name == null ? null : name.trim()); ConnectionPoolManager realPool = poolMap.remove(name); if (realPool != null) { MBeanFacade.unregisterMBean(name); realPool.remove(); } } /** * Remove the pool */ void remove() { Map<String, ConnectionPoolManager> tempMap = poolMap; // reset pool map poolMap = new HashMap<String, ConnectionPoolManager>(); for (Entry<String, ConnectionPoolManager> e : tempMap.entrySet()) { String poolName = e.getKey(); MBeanFacade.unregisterMBean(poolName); ConnectionPoolManager pool = e.getValue(); pool.remove(); } } }