package com.lizard.fastdb.datasource;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.lizard.fastdb.config.Config;
import com.lizard.fastdb.connection.ConnectionProvider;
/**
* 数据源管理类 负责加载数据源,获得数据源,更新数据源,销毁数据源,销毁连接池等
*
* @author SHEN.GANG
*/
public class DataSourceManager
{
private static final Log logger = LogFactory.getLog(DataSourceManager.class);
private static final String DS_CONFIG_XML = "datasource.xml"; // 数据源XML配置文件名称
private volatile static boolean HAS_STARTUP = false; // 用于控制startup方法的调用,该方法仅允许被调用一次
/**
* 用于静态数据源获得ConnectionProvider
*
* @param ds_name 数据源名称
* @return 数据源对应的ConnectionProvider
*/
public synchronized static void initConnectionProvider(String ds_name)
{
ConnectionProvider connP = DataSourceCache.getConnectionProvider(ds_name);
// 如果缓存中没有当前数据源的ConnectionProvider对象,说明该数据源没有被加载
if (connP == null)
{
// 查看当前数据源是否在缓存中
Properties ds = DataSourceCache.getDataSource(ds_name);
// 缓存中没有该数据源可能是没有加载导致的
if (ds == null)
{
loadDataSourceConfig();
ds = DataSourceCache.getDataSource(ds_name);
}
// 如果还是为null表示数据源不存在
if (ds == null)
{
throw new DataSourceException("DataSource named [" + ds_name + "] is not exist!");
}
connP = DataSourceCache.createConnectionProvider(ds);
loadOnStartup();
}
connP = null;
}
/**
* 用于动态数据源获得ConnnectionProvider
*
* @param ds 数据源配置
* @return 数据源对应的ConnectionProvider
*/
public synchronized static void initConnectionProvider(Properties ds)
{
String ds_name = ds.getProperty("name");
ConnectionProvider connP = DataSourceCache.getConnectionProvider(ds_name);
// 为null表示数据源不存在或未加载
if (connP == null)
{
// 查看是否存在配置重复的数据源
String repeat = DataSourceCache.isDataSourceRepeated(ds);
// 没有重复配置的数据源
if (repeat == null)
{
DataSourceCache.putDataSource(ds);
DataSourceCache.putLinkmapping(ds_name, ds_name);
DataSourceCache.createConnectionProvider(ds);
}
// 存在重复配置的数据源
else
{
logger.info("DataSource [" + repeat + "] is duplicate to DataSource [" + ds_name + "]!We make the softlink [" + ds_name + " --> "
+ repeat + "] to alternative it!");
DataSourceCache.putLinkmapping(ds_name, repeat);
// 判断数据源是否被加载
connP = DataSourceCache.getConnectionProvider(repeat);
if (connP == null)
{
DataSourceCache.createConnectionProvider(DataSourceCache.getDataSource(repeat));
}
}
connP = null;
}
// 不为null表示当前数据源名称对应的数据源已加载
else
{
Properties ds_cache = DataSourceCache.getDataSource(ds_name);
// 如果缓存中的数据源配置与当前的数据源配置不同,表示数据源名称重复
if (!DataSourceUtil.isSameDataSource(ds, ds_cache))
{
throw new DataSourceException("DataSource named [" + ds_name + "] is exist!Exist datasource url["
+ ds_cache.getProperty("driver-url") + "].");
}
}
}
/**
* 加载所有数据源,仅用于监听器调用
*/
public synchronized static void startup()
{
if (HAS_STARTUP)
{
logger.info("DataSource config has been loaded already!");
return;
}
HAS_STARTUP = true;
logger.info("Begin Loading All DataSource");
loadDataSourceConfig();
loadOnStartup();
}
/**
* 加载数据源配置到缓存
*/
private static void loadDataSourceConfig()
{
// 加载所有配置的数据源到缓存
Config config = new Config(DS_CONFIG_XML);
for (Properties ds : config.getDatasources())
{
DataSourceCache.putDataSource(ds);
}
for (Map.Entry<String, String> entry : config.getLinkmapping().entrySet())
{
DataSourceCache.putLinkmapping(entry.getKey(), entry.getValue());
}
config = null;
}
/**
* 加载load-on-startup为true的数据源
*/
private static void loadOnStartup()
{
Set<Map.Entry<String, Properties>> entrySet = DataSourceCache.getDataSourcePool().entrySet();
for (Map.Entry<String, Properties> entry : entrySet)
{
String name = entry.getKey();
Properties ds = entry.getValue();
if (Boolean.valueOf(ds.getProperty("load-on-startup")) && DataSourceCache.getConnectionProvider(name) == null)
{
logger.info("Register [" + name + "] DataSource ...");
// 创建ConnectionProvider配置数据源,并将ConnectionProvider放入缓存
DataSourceCache.createConnectionProvider(ds);
}
}
}
/**
* 彻底销毁指定的数据源,该数据源以后不能再被使用。
*
* @param ds_name 待销毁的数据源名称
*/
public static void destroy(String ds_name)
{
destroy(ds_name, true);
}
/**
* 关闭一个数据源连接池,释放里面创建的所有物理连接
*
* @param ds_name 待关闭的数据源名称
*
* @author SHEN.GANG
*/
public static void shutdown(String ds_name)
{
destroy(ds_name, false);
}
/**
* 销毁全部连接池,清理数据源、软连接和ConnectionProvider缓存
*/
public synchronized static void shutdown()
{
logger.info("--- Shutdown Connection Pool ---");
// 获取系统中注册的所有连接池对象
Map<String, ConnectionProvider> pool = DataSourceCache.getConnectionProviderPool();
if (pool == null || pool.isEmpty())
{
return;
}
Set<Map.Entry<String, ConnectionProvider>> entrySet = pool.entrySet();
// 每种ConnectionProvider的shutdown方法只被调用一次
// Set<String> hasShutdown = new HashSet<String>();
ConnectionProvider connP = null;
for (Map.Entry<String, ConnectionProvider> entry : entrySet)
{
connP = entry.getValue();
// 下面的方式存在潜在的bug:下面设定了每个ConnectionProvider类名称只能调用一次:
// 对于Proxool有一个 ProxoolFade.shutdown() 关闭所有的不同的数据源连接池,
// 而对于 C3P0 和 BoneCP 来说,则没有这样的方法,它们每次只能关闭当前一个对应的数据源连接池,
// 如果采用下面的限定,则会造成多个同类型数据源(一种连接池提供商下的多个数据源)连接池对象只有一个被销毁,进而造成其它连接池对象无法被销毁,而泄露物理连接。
// 所以,必须对所有的数据源对象逐个关闭。
// String connP_class = connP.getClass().getName();
// if (hasShutdown.contains(connP_class))
// {
// continue;
// }
// hasShutdown.add(connP_class);
if (null != connP)
{
// 分别调用ConnectionProvider的shutdown方法关闭当前这个连接池
connP.shutdown();
connP = null;
}
}
// 清理缓存
DataSourceCache.clean();
// hasShutdown.clear();
// hasShutdown = null;
}
/**
* 销毁指定的数据源<br>
* 1、如果传递的是真实数据源名称,将销毁对应的真实数据源;<br>
* 2、如果传递的是虚拟数据源名称,将销毁软连接,软连接指向的真实数据源不会被销毁;
*
* @param ds_name 待销毁的数据源名称
* @param isCompleted true -- 表示彻底销毁这个数据源,以后不能再被使用;
* false -- 表示只释放数据源连接池中创建的所有物理连接,以后可以被再次创建使用。
*/
private synchronized static void destroy(String ds_name, boolean isCompleted)
{
if(isCompleted) {
logger.info("Destory [" + ds_name + "] DataSource ...");
} else {
logger.info("Shutdown [" + ds_name + "] DataSource ...");
}
String real = DataSourceCache.getFinalLinkTo(ds_name);
// 销毁的是真实数据源
if (real != null && real.equals(ds_name))
{
ConnectionProvider connP = DataSourceCache.getConnectionProvider(real);
if( null == connP )
{
if(isCompleted) {
logger.warn("Warning: You attempt to destroy a datasource[" + ds_name + "] that does not exist, maybe it has been destroyed.");
} else {
logger.warn("Warning: You attempt to shutdown a datasource[" + ds_name + "] that does not exist, maybe it has been shut down.");
}
return;
}
// ConnectionProvider.destory 方法废弃,由 shutdown()替代
// connP.destory();
connP.shutdown();
// 只有当为 true -- 彻底销毁时,才清除对应的缓存数据
if(isCompleted) {
DataSourceCache.evictDataSource(real);
}
DataSourceCache.evictConnectionProvider(real);
}
// 销毁的是软连接
else
{
// 只有当为 true -- 彻底销毁时,才销毁软连
if(isCompleted) {
DataSourceCache.evictLinkmapping(ds_name);
}
}
}
}