package org.itscool.commons.connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.itscool.commons.pool.ObjectPool;
import org.itscool.commons.pool.ObjectPoolEventListener;

/**
 * 独自のコネクションプールとTransactionインスタンスの生成を管理するクラスです<BR>
 * <BR>
 * ConnectionPoolはDBとの接続に一番時間がかかる点を考えて、いったん作った接続は
 * すぐにクローズせずに使いまわすという方法です。<BR>
 * 使う側はコネクションプールに空いているコネクションがないか確認し、
 * あればそれを、なければ最大数に達するまで新規にコネクションを張ってそれを使う
 * という手順になります。<BR>
 *
 * アプリケーションの初期化と終了あるいはServletもしくはFilterのinit()とdestroy()
 * 等でConnectionMangerの初期化と終了処理を実装するようにしてください<P>
 *
 * ConnectionManager manager = ConnectionManger.getInstance();<P>
 *
 * manager.init();<B>
 * manager.destroy();<B>
 *
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
 */
public class JdbcConnectionManager implements ConnectionManager, ObjectPoolEventListener{
    private static JdbcConnectionManager instance = null;
    private ObjectPool pool;				//オブジェクトプール
    private String drvname;                // JDBCドライバ名
    private String pwd;                    // パスワード
    private String url;                    // URL
    private String user;                  // ユーザ名
    private int timeout;                  // Connection取得時のタイムアウト
    private boolean initFlg = false;     //初期化フラグ
    private int initSize = 0;				//プールの初期化サイズ
    private int maxSize = 1;				//プールのMAXサイズ
    private boolean mode = false;        // AutoCommitモード
    private boolean showSql = false;		//SQL表示フラグ
    
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("Driver Name:").append(drvname);
        sb.append(", ");
        sb.append("timeout:").append(timeout);
        sb.append(", ");
        sb.append("initSize:").append(initSize);
        sb.append(", ");
        sb.append("maxSize:").append(maxSize);
        sb.append(", ");
        sb.append("mode:").append(mode);
        sb.append(", ");
        sb.append("showSql:").append(showSql);
        return sb.toString();
    }
    
    /**
     * 唯一のConnectionManagerインスタンスを取得します
     * @return 唯一のConnectionMangerインスタンスを返します
     */
    public static JdbcConnectionManager getInstance(){
        if( instance == null){
            instance = new JdbcConnectionManager();
        }
        return instance;
    }
    
    /**
     * データベース接続マネージャを初期化します
     * @param url データベース接続URL
     * @param user ユーザ名
     * @param pwd パスワード
     * @param drvname JDBCドライバ名
     * @param initSize 初期プールサイズ
     * @param maxSize 最大プールサイズ
     * @param mode AutoCommitモード
     * @param timeout Connectionインスタンス取得時のタイムアウト.
     * (-1)を指定した場合はタイムアウト無し
     * @throws TransactionException
     */
    public void init(String url, String user, String pwd, String drvname,
        int initSize, int maxSize, boolean mode, int timeout, boolean showSql) throws TransactionException{
        pool = new ObjectPool(this, initSize, maxSize);
        
        this.url = url;
        this.user = user;
        this.pwd = pwd;
        this.mode = mode;
        this.drvname = drvname;
        this.timeout = timeout;
        this.showSql = showSql;
        this.initSize = initSize;
        this.maxSize = maxSize;
        for( int i=0; i<initSize; i++ ){
            pool.addNewElement( (Object)newTransaction() );
        }
        initFlg = true;
    }
    
    public void init(){
        pool = new ObjectPool(this, initSize, maxSize);
        for( int i=0; i<initSize; i++ ){
            pool.addNewElement( (Object)newTransaction() );
        }
    }
    
    /**
     * 新規コネクションの作成
     * @return Connectionインスタンスをプールから取得します
     * @throws TransactionException
     */
    private Transaction newTransaction() throws TransactionException{
        Connection con = null;
        Transaction trn = null;
        try {
            if(drvname==null || drvname.length() == 0){
                throw new TransactionException("jdbc name is null.");
            }else if(url ==null || url.length() == 0){
                throw new TransactionException("connection url is null.");
            }
            Class.forName(drvname);
            con = DriverManager.getConnection(url, user, pwd);
//add 2006/12/19 kano start プロクシに寄るResultSet、PrepardStatementの自動クローズ処理追加対応
            con = ProxyConnection.createProxy(con);
//add 2006/12/19 kano end プロクシに寄るResultSet、PrepardStatementの自動クローズ処理追加対応
            con.setAutoCommit(mode);
            trn = new Transaction(con);
            trn.setShowSql(showSql);
        }catch(ClassNotFoundException e){
            throw new TransactionException(e);
        }catch (SQLException se){
            throw new TransactionException(se);
        }
        return trn;
    }
    
    /**
     * ConnectionManagerインスタンスが初期化されているか判定します
     * @return 初期化済みの場合はtrueを返します
     */
    public boolean isInit(){
        return initFlg;
    }
    
    /**
     * Transactionインスタンスをプールから取得します
     * プールにTransactionインスタンスがない場合はnullを返します
     * @return Transactionインスタンスを返します
     */
    public Transaction getTransaction(){
        if( timeout > 0 ){
            return (Transaction)pool.getElement(timeout);
        }else{
            return (Transaction)pool.getElement();
            
        }
    }
    
    /**
     * 不要になったTransactionインスタンスをプールに返します
     * @param trn 不要になったTransactionインスタンス
     */
    public void returnTransaction(Transaction trn){
        if(trn == null ){
            trn = newTransaction();
        }else if(trn.isClosed()){
            trn = newTransaction();
        }
        pool.returnElement(trn);
    }
    
    /**
     * コネクションプールを開放し、終了します。
     */
    public void destroy(){
        if(pool != null ){
            pool.release();
        }
        pool = null;
        initFlg = false;
    }
    
    /**
     * org.itscool.commons.pool.Poolクラスから要素の追加を要求
     * するために呼ばれるイベントです<br>
     * 新しいConnectionインスタンスを生成し、Poolクラスに返します<br>
     * org.itscool.commons.pool.Poolクラス以外からこのメソッドを
     * 呼ばないでください
     */
    public Object doMissingElementEvent(){
        Transaction trn = newTransaction();
        return trn;
    }
    
    /**
     * org.itscool.commons.pool.Poolクラスから呼ばれる
     * イベントです<br>
     * 指定されたConnection要素のクローズ処理を実施します<br>
     * org.itscool.commons.pool.Poolクラス以外からこのメソッドを
     * 呼ばないでください
     * @param obj 指定された要素
     */
    public void doReleaseElementEvent(Object obj){
        Transaction trn = (Transaction) obj;
        try {
            trn.close();
        }catch (TransactionException e) {
            throw new TransactionException(e);
        }
    }
    
    /**
     * 現在のプールサイズを返します
     * @return 現在のプールサイズを返します
     */
    public int getPoolSize(){
        if( pool != null ){
            return pool.getPoolSize();
        }else{
            return -1;
        }
    }
    
    /**
     * プールの初期サイズを返します
     * @return プールの初期サイズを返します
     */
    public int getInitSize(){
        return initSize;
    }
    
    /**
     * プールの拡張可能な最大サイズを返します
     * @return 拡張可能なプールサイズを返します
     */
    public int getMaxSize(){
        return maxSize;
    }
    
    /**
     * JDBCドライバ名をセットします
     * @param drvname JDBCドライバ名
     */
    public void setDrvname(String drvname) {
        this.drvname = drvname;
    }
    
    /**
     * AutoCommitモードをセットします
     * @param mode AutoCommitモード
     */
    public void setMode(boolean mode) {
        this.mode = mode;
    }
    
    /**
     * データベースコネクション時のパスワードをセットします
     * @param pwd パスワード
     */
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    
    /**
     * SQL表示モードをセットします
     * @param showSql SQL表示モード
     */
    public void setShowSql(boolean showSql) {
        this.showSql = showSql;
    }
    
    /**
     * コネクションタイムアウトをセットします
     * @param timeout コネクションタイムアウト
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }
    
    /**
     * JDBC接続先URLをセットします
     * @param url JDBC接続先URL
     */
    public void setUrl(String url) {
        this.url = url;
    }
    
    /**
     * JDBC接続ユーザをセットします
     * @param user
     */
    public void setUser(String user) {
        this.user = user;
    }
    
    /**
     * コネクションプールの初期サイズをセットします
     * @param initSize コネクションプールの初期サイズ
     */
    public void setInitSize(int initSize) {
        this.initSize = initSize;
    }
    
    /**
     * コネクションプールの最大サイズをセットします
     * @param maxSize
     */
    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }
    
    /**
     * JDBCドライバ名を取得します
     * @return JDBCドライバ名
     */
    public String getDrvname() {
        return drvname;
    }
    
    /**
     * ConnectionMangerインスタンスの初期化状況を取得します
     * @return 初期済みの場合はtrueを返します
     */
    public boolean isInitFlg() {
        return initFlg;
    }
    
    /**
     * AutoCommitModeを取得します
     * @return AutoCommitModeが有効になっている場合はtureを返します
     */
    public boolean getMode() {
        return mode;
    }
    
    /**
     * データベース接続時のパスワードを取得します
     * @return データベース接続時のパスワード
     */
    public String getPwd() {
        return pwd;
    }
    
    /**
     * SQL表示モードを取得します
     * @return SQL表示の場合はtrueを返します
     */
    public boolean getShowSql() {
        return showSql;
    }
    
    /**
     * コネクションタイムアウトを取得します
     * @return コネクションタイムアウトをmsecで取得します
     */
    public int getTimeout() {
        return timeout;
    }
    
    /**
     * データベース接続先URLを取得します
     * @return データベース接続先URL
     */
    public String getUrl() {
        return url;
    }
    
    /**
     * データベース接続ユーザを取得します
     * @return データベース接続ユーザ
     */
    public String getUser() {
        return user;
    }
    
}

