package mc;
import java.util.*;
import java.util.Date;
import java.sql.*;
import java.io.*;
public class DBConnectionManager {
/** 日志文件写入流对象 */
private PrintWriter log;
/** 存放连接池对象Hashtable */
private Hashtable pools;
/** DBConnectionManager 唯一实例 */
static private DBConnectionManager instance;
/** 记录客户机的连接数 */
static private int remotes;
/** 存放数据库连接驱动 */
private Vector drivers;
/**
* 类名: DBConnectionManager<br>
* 函数功能: 私有构造方法<br>
* 返回值: 无<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private DBConnectionManager()
{
init();
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 初始化类属性<br>
* 返回值: 无<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private void init()
{
pools = new Hashtable();
drivers = new Vector();
//建立属性对象
Properties prop = new Properties();
//把属性文件的属性列表读到属性对象
try {
InputStream is = getClass().getResourceAsStream("db.properties");
prop.load(is);
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取属性文件错误!");
}
// 建立日志文件输入流对象
String logFilePath = prop.getProperty("logFile");
try {
log = new PrintWriter(new FileWriter(logFilePath,true),true);
} catch (IOException e) {
e.printStackTrace();
System.out.println("无法打开日志文件:" + logFilePath);
log = new PrintWriter(System.err);
}
// 加载和注册数据库驱动
loadDriver(prop);
// 初始化和创建连接池实例
createPools(prop);
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 返回 DBConnectionManager 唯一实例.如果是第一次调用此方法,则创建实例<br>
* 返回值: DBConnectionManager 对象<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
static synchronized public DBConnectionManager getInstance()
{
if(instance == null){
instance = new DBConnectionManager();
}
remotes++;
return instance;
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 把文本信息写入日志文件<br>
* 返回值: 无<br>
* 参数说明: String message 信息描述字符串<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private void logWriter(String message)
{
log.println("日期: " + new Date() + message);
}
private void logWriter(Throwable e,String errMessage)
{
log.println("日期: " + new Date() + errMessage + "\n错误: ");
e.printStackTrace(log); // 把异常描述写入日志文件
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 获取一个数据库连接<br>
* 返回值: Connection类型<br>
* 参数说明: String poolName 接收连接池名称<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public Connection getConnection(String poolName)
{
// 通过连接池名称返回连接池对象
DBConnectionPool pool = (DBConnectionPool) pools.get(poolName);
if(pool == null){
logWriter("试图使用不存在的连接池" + poolName);
return null;
}
// 返回连接给用户.
return pool.getConnection();
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 释放连接,把用完的连接归还给连接池<br>
* 返回值: 无<br>
* 参数说明: String poolName 连接池名称,Connection conn 连接对象<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public void RefeaseConnection(String poolName,Connection conn)
{
DBConnectionPool pool = (DBConnectionPool) pools.get(poolName);
if(pool == null){
logWriter("释放连错误!请检查连接池名称.");
return;
}else{
pool.freeConnection(conn);
}
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 释放连接,把用完的连接归还给连接池<br>
* 返回值: 无<br>
* 参数说明: String poolName 连接池名称,Connection conn 连接对象<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public void RefeaseConnection
(String poolName,Connection conn,Statement stn,PreparedStatement ptn,ResultSet result)
{
try {
if (stn != null)
stn.close();
if (ptn != null)
ptn.close();
if (result != null)
result.close();
}catch(Exception ex)
{
ex.printStackTrace(System.out);
System.out.println(ex.getMessage());
}
DBConnectionPool pool = (DBConnectionPool) pools.get(poolName);
if(pool == null){
logWriter("释放连错误!请检查连接池名称.");
return;
}else{
pool.freeConnection(conn);
}
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 释放所有的连接,注销驱动<br>
* 返回值: 无<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public synchronized void releas()
{
if(--remotes != 0){
return;
}
Enumeration ePools = pools.elements();
while(ePools.hasMoreElements()){
DBConnectionPool pool = (DBConnectionPool) ePools.nextElement();
//关闭所有的连接对象
pool.release();
}
Enumeration eDrivers = drivers.elements();
while(eDrivers.hasMoreElements()){
Driver driver = (Driver) eDrivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logWriter("注销" + driver.getClass().getName() + "成功!");
} catch (SQLException e) {
logWriter(e,"无法注销" + driver.getClass().getName() + "驱动!");
e.printStackTrace();
}
}
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 加载和注册数据库驱动程序<br>
* 返回值: 无<br>
* 参数说明: Properties prop变量接收数据库属性列表<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private void loadDriver(Properties prop)
{
// 从prop属性对象中取出驱动字符串
String driverClass = prop.getProperty("drivers");
// 把驱动字符串分解为标记
StringTokenizer st = new StringTokenizer(driverClass);
while(st.hasMoreTokens()){
String driverClassName = st.nextToken().trim();
try {
// 加载数据库驱动
Driver driver = (Driver) Class.forName(driverClassName).newInstance();
// 注册数据库驱动
DriverManager.registerDriver(driver);
drivers.addElement(driver);
// 把注册相关信息写入日志文件
logWriter("成功注册数据库驱动.");
} catch (InstantiationException e) {
logWriter("加载数据库驱动错误:" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
logWriter("加载数据库驱动错误:" + e.getMessage());
e.printStackTrace();
} catch (ClassNotFoundException e) {
logWriter("加载数据库驱动错误:" + e.getMessage());
e.printStackTrace();
} catch (SQLException e) {
logWriter("无法数据库驱动:" + e.getMessage());
e.printStackTrace();
}
}
}
/**
* 类名: DBConnectionManager<br>
* 函数功能: 初始化和建立连接池实例<br>
* 返回值: 无<br>
* 参数说明: Properties prop 变量接收设置连接池属性列表;如 最大连接数,最少连接数等<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private void createPools(Properties prop)
{
/** 返回属性列表中所有键的枚举 */
Enumeration propNames = prop.propertyNames();
while(propNames.hasMoreElements()){
String name = (String) propNames.nextElement();
if(name.endsWith(".url")){
// propName 以后用做连接池名称
String poolName = name.substring(0,name.indexOf("."));
String url = prop.getProperty(poolName + ".url").trim();
if(poolName == null || url == null){
log.print("没有连接池:" + poolName + " 指定的URL");
continue;
}
String user = prop.getProperty(poolName + ".user").trim();
String password = prop.getProperty(poolName + ".password").trim();
String strMaxConn = prop.getProperty(poolName + ".maxConn").trim();
String strMinConn = prop.getProperty(poolName + ".minConn").trim();
int maxConn = 0,minConn = 0;
try{
maxConn = Integer.parseInt(strMaxConn);
minConn = Integer.parseInt(strMinConn);
}catch(NumberFormatException ne){
maxConn = 100;
minConn = 50;
logWriter(ne," 属性文件中的最大连接数错误,请检查书写是否正确!" +
"此错误出现后,系统自动设置最大连接数为100");
}
//取得数据库属性文件中的相关设置后开始建立连接池
DBConnectionPool pool = new DBConnectionPool(poolName,url,user,password,maxConn,minConn);
//把连接池放到 Hashtable
pools.put(poolName, pool);
logWriter("连接池创建成功!");
}
}
}
/**
* 类名: DBConnectionPool类是DBConnectionManager类的内部类<br>
* 内部类功能: 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最大连接数为止.
在返回连接给客户程序之前,它能够验证连接的有效性.<br>
* 返回值: 无<br>
* 参数说明: <br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
class DBConnectionPool
{
/** 记录分出的连接数 */
private int checkOut;
/** 存放连接的 Vector 对象 */
private Vector freeConnection;
/** 数据库帐户 */
private String user;
/** 登陆密码 */
private String password;
/** 连接池名称 */
private String poolName;
/** 数据源URL */
private String url;
/** 最大连接数 */
private int maxConn;
/** 最小连接数 */
private int minConn;
/**
* 类名: DBConnectionPool<br>
* 函数功能: 构造方法<br>
* 返回值: 无<br>
* 参数说明: String poolName 接收连接池名称; String url 数据源URL;
String user 数据库帐户; String password 密码; int maxConn 设置最大的连接数<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public DBConnectionPool(String poolName, String url, String user,
String password, int maxConn,int minConn) {
freeConnection = new Vector();
this.poolName = poolName;
this.url = url;
this.user = user;
this.password = password;
this.maxConn = maxConn;
this.minConn = minConn;
init();
}
/**
* 类名: DBConnectionPool<br>
* 函数功能: 该方法在程序第一次运行时生成最小连接数。<br>
* 返回值: Connection 对象<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
private void init() {
Connection conn = null;
for (int i = 0; i < minConn; i++) {
try {
conn = DriverManager.getConnection(url, user, password);
logWriter("连接池: " + poolName + " 创建了一个新连接");
} catch (SQLException e) {
logWriter(e, "无法建立:" + url + "连接");
e.printStackTrace();
}
freeConnection.addElement(conn);
}
}
/**
* 类名: DBConnectionPool<br>
* 函数功能: 返回连接对象<br>
* 返回值: Connection 对象<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public Connection getConnection(){
Connection conn = null;
if(freeConnection.size() > 0){
//当连接池中有空闲的连接,就从池中取出一条连接
conn = (Connection) freeConnection.firstElement();
//连接取出后,从连接池中删除该连接的记录.
freeConnection.removeElementAt(0);
try {
//判断所取出的连接是否有效
if(conn.isClosed()){
logWriter("从连接池 "+ poolName +" 中删除一条无效的连接");
//递归调用自己,获取可用的连接
conn = getConnection();
}
} catch (SQLException e) {
logWriter("从连接池 "+ poolName +" 中删除一条无效的连接");
e.printStackTrace();
//递归调用自己,获取可用的连接
conn = getConnection();
}
// 当连接池中没有可用的连接,且当前的连接数在设置最大的连接数以下.新建一个连接
}else if(checkOut < maxConn){
conn = newConnection();
// 当连接池中没有可用的连接,且当前连接已经达到最大连接数.
}else{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//等待500毫秒后,递归调用自己,获取可用的连接
conn = getConnection();
}
// 记录分出去的连接数
checkOut++;
return conn;
}
/**
* 类名: DBConnectionPool<br>
* 函数功能: 返回新建的连接对象<br>
* 返回值: Connection 对象<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
public Connection newConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(url,user,password);
logWriter("连接池: " + poolName + " 创建了一个新连接");
} catch (SQLException e) {
logWriter(e,"无法建立:" + url + "连接");
e.printStackTrace();
}
return conn;
}
/**
* 类名: DBConnectionPool<br>
* 函数功能: 释放连接对象<br>
* 返回值: 无<br>
* 参数说明: 无<br>
* 创建人: andy<br>
* 创建时间: 2006年9月1日
* 最后修改: 无<br>
*/
synchronized public void freeConnection(Connection conn)
{
// 把连接对象放回 Vector 里
freeConnection.addElement(conn);
// 连接数减1
checkOut--;
// 唤醒在此对象监视器上等待的所有线程
notifyAll();
}
public synchronized void release()
{
Enumeration allConn = freeConnection.elements();
while(allConn.hasMoreElements()){
Connection conn = (Connection) allConn.nextElement();
try {
conn.close();
logWriter("成功关闭连接!");
} catch (SQLException e) {
logWriter(e,"无法关闭连接池" + poolName + "中连接!");
e.printStackTrace();
}
//清空 Vector 中的所有元素
freeConnection.removeAllElements();
}
}
}
}