package com.lizard.fastdb.connection.proxool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Properties;
import javax.transaction.TransactionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.enhydra.jdbc.standard.StandardXADataSource;
import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.ProxoolFacade;
import com.lizard.fastdb.connection.ConnectionProvider;
import com.lizard.fastdb.datasource.DataSourceUtil;
/**
* ConnectionProvider的 Proxool 实现类
*
* @author SHEN.GANG
*/
public class ProxoolConnectionProvider implements ConnectionProvider
{
private static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(ProxoolConnectionProvider.class);
private Properties prop = null; // 当前ConnectionProvider对应的数据源
private String name = null; // 当前数据源名称
private int connectionTimeOut = 0; // 设置驱动程序试图连接到某一数据库时将等待的最长时间,以秒为单位
private int acquireRetryAttempts = 0; // 重试次数
private long acquireRetryDelay = 0; // 每次重试的间隔时间
/**
* 根据给定的数据源配置信息初始化数据库连接池
*
* @param prop 数据源配置信息
*/
public void configure(Properties prop)
{
if (this.prop != null)
{
return;
}
this.prop = prop;
this.name = this.prop.getProperty("name");
this.connectionTimeOut = Integer.valueOf(this.prop.getProperty("connection-timeout"));
this.acquireRetryAttempts = Integer.parseInt(this.prop.getProperty("acquire-retry-attempts"));
this.acquireRetryDelay = Long.parseLong(this.prop.getProperty("acquire-retry-delay"));
// 未注册
if (!isDataSourceRegistered(name))
{
DriverManager.setLoginTimeout(connectionTimeOut);
String url = "proxool." + name + ":" + this.prop.getProperty("driver-class") + ":" + this.prop.getProperty("driver-url");
try
{
ProxoolFacade.registerConnectionPool(url, convertToProxoolProperties(this.prop));
logger.info("Register proxool pool [" + name + "] success!");
}
catch (ProxoolException e)
{
logger.info("Fail to register proxool pool [" + name + "]!");
e.printStackTrace();
}
}
}
/**
* 根据数据源名称获取数据库连接
*
* @return 数据库连接
* @throws SQLException
*/
@SuppressWarnings("static-access")
public Connection getConnection()
{
int _acquireRetryAttempts = acquireRetryAttempts;
long _acquireRetryDelay = acquireRetryDelay;
Connection conn = null;
boolean tryAgain = false;
do
{
try
{
conn = DriverManager.getConnection("proxool." + prop.getProperty("name"));
tryAgain = false;
logger.debug("Successfully establish connectoin to DB");
}
catch (SQLException e)
{
// 如果重试次数大于0,则重试次数减1后,休眠一段时间后再次重试
if (_acquireRetryAttempts-- > 0)
{
logger.error("Failed to aquire connection. Sleep for " + _acquireRetryDelay + " ms. Attempts left: " + _acquireRetryAttempts, e);
try
{
Thread.currentThread().sleep(_acquireRetryDelay);
tryAgain = true;
}
catch (InterruptedException e2)
{
tryAgain = false;
logger.error("Thread occur InterruptedException, stop to aquire connection", e2);
}
}
else
{
tryAgain = false;
}
}
}
while (tryAgain);
return conn;
}
/**
* 根据指定的数据源获得该数据源的事务Connection对象,并将其设置给指定的事务管理器管理
*
* @param tm 事务管理器
* @return 由XAConnection创建的Connection对象
* @throws SQLException
*/
public Connection getXAConnection(TransactionManager tm) throws SQLException
{
StandardXADataSource sxd = DataSourceUtil.convertPropertiesToXADataSource(prop);
sxd.setTransactionManager(tm);
Connection conn = sxd.getXAConnection().getConnection();
conn.setAutoCommit(false);
return conn;
}
/**
* 关闭数据库连接
*
* @param conn 要关闭的连接
* @throws SQLException
*/
public void closeConnection(Connection conn) throws SQLException
{
if (conn != null)
{
conn.close();
}
}
/**
* 销毁数据源
*/
private void destory()
{
// 已注册
if (isDataSourceRegistered(name))
{
try
{
ProxoolFacade.removeConnectionPool(name);
logger.info("Destroy proxool pool [" + name + "] success!");
}
catch (ProxoolException e)
{
logger.info("Fail to destroy proxool pool [" + name + "]!");
e.printStackTrace();
}
this.prop = null;
this.name = null;
}
}
/**
* 关闭连接池
*/
public void shutdown()
{
// 销毁当前数据源连接池
destory();
// 如果当前没有其它Proxool数据源连接池存在的话,则调用 shutdown() 彻底销毁整个Proxool连接池(包括销毁守护线程等资源)
String[] aliases = ProxoolFacade.getAliases();
if (null != aliases && aliases.length == 0)
{
ProxoolFacade.shutdown();
}
}
/**
* 检查指定名称的数据源是否已经注册
*
* @param name 数据源名称
* @return true -- 已注册,false -- 未注册
*/
private boolean isDataSourceRegistered(String name)
{
// 获取系统已经注册的数据源信息
String[] aliases = ProxoolFacade.getAliases();
if (aliases == null || aliases.length == 0)
{
return false;
}
for (int i = 0; i < aliases.length; i++)
{
if (aliases[i] != null && name != null && aliases[i].equalsIgnoreCase(name))
{
return true;
}
}
return false;
}
/**
* 将通用Properties转换为Proxool使用的Properties
*
* @param ds 通用Properties
* @return Proxool转用Properties
*/
private Properties convertToProxoolProperties(Properties ds)
{
Properties prop = new Properties();
// 以下属性不支持
// init-connection-size
// acquire-increment-size
// max-connection-idletime
@SuppressWarnings("rawtypes")
Enumeration names = ds.propertyNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
String value = ds.getProperty(name);
if ("user".equals(name) || "password".equals(name))
{
prop.setProperty(name, value);
}
else if ("driver-url".equals(name))
{
prop.setProperty("driverurl", value);
}
else if ("driver-class".equals(name))
{
prop.setProperty("driverclass", value);
}
else if ("max-connection-size".equals(name))
{
prop.setProperty("proxool.maximum-connection-count", value);
}
else if ("min-connection-size".equals(name))
{
prop.setProperty("proxool.minimum-connection-count", value);
}
else if ("available-connection-size".equals(name))
{
prop.setProperty("proxool.prototype-count", value);
}
else if ("max-connection-lifetime".equals(name))
{
prop.setProperty("proxool.maximum-connection-lifetime", String.valueOf(Integer.parseInt(value) * 1000));
}
else if ("test-connection-checkin".equals(name))
{
prop.setProperty("proxool.test-after-use", value);
}
else if ("test-connection-checkout".equals(name))
{
prop.setProperty("proxool.test-before-use", value);
}
else if ("test-sql".equals(name))
{
prop.setProperty("proxool.house-keeping-test-sql", value);
}
else
{
prop.setProperty("proxool." + name, value);
}
}
return prop;
}
}