package org.maachang.bdb ;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;

/**
 * Berkeley DB wrapper 管理.
 *  
 * @version 2010/01/02
 * @author  masahito suzuki
 * @since   bdb 1.0.0
 */
public class BdbManager {
    
    /** 遅延書き込みモード. **/
    protected static boolean _DEFERRED_WRITE = true ;
    
    /** 展開ディレクトリ名. **/
    protected final AtomicOBJ<String> dirName = new AtomicOBJ<String>() ;
    
    /** BDB環境. **/
    protected final AtomicOBJ<Environment> env = new AtomicOBJ<Environment>() ;
    
    /** BDB管理. **/
    protected final ConcurrentHashMap<String,Bdb> manager = new ConcurrentHashMap<String,Bdb>() ;
    
    /** BMQ管理. **/
    protected final ConcurrentHashMap<String,BQueue> managerq = new ConcurrentHashMap<String,BQueue>() ;
    
    /**
     * スレッド毎に対して、BdbKeyValueを保持.
     */
    private static final ThreadLocal<BdbKeyValue> LO = new ThreadLocal<BdbKeyValue>() ;
    
    /**
     * BdbKeyValueオブジェクトを取得.
     * <p>※このオブジェクトは、ThreadLocalから取得されているので、Threadセーフです.</p>
     * @return BdbKeyValue BdbKeyValueオブジェクトが返されます.
     */
    public static final BdbKeyValue getBdbKeyValue() {
        BdbKeyValue ret = LO.get() ;
        if( ret == null ) {
            ret = new BdbKeyValue() ;
            LO.set( ret ) ;
        }
        else {
            ret.clear() ;
        }
        return ret ;
    }
    
    /**
     * コンストラクタ.
     * @exception Exception 例外.
     */
    public BdbManager() throws Exception {
        this( null ) ;
    }
    
    /**
     * コンストラクタ.
     * @param dir BDB作成用フォルダを設定.
     * @exception Exception 例外.
     */
    public BdbManager( String dir ) throws Exception {
        this( true,dir ) ;
    }
    
    /**
     * コンストラクタ.
     * @param mode [true]の場合、遅延書き込みモードはONになります.
     * @param dir BDB作成用フォルダを設定.
     * @exception Exception 例外.
     */
    public BdbManager( boolean mode,String dir ) throws Exception {
        if( dir == null || ( dir = dir.trim() ).length() <= 0 ) {
            dir = "." ;
        }
        File f = new File( dir ) ;
        if( !f.isDirectory() ) {
            if( !f.mkdirs() ) {
                throw new IOException( "ディレクトリの作成に失敗しました:" + dir ) ;
            }
        }
        BdbManager._DEFERRED_WRITE = mode ;
        EnvironmentConfig conf = new EnvironmentConfig() ;
        conf.setTransactional(false) ;// トランザクションなし.
        conf.setAllowCreate(true) ;// 新しく作成.
        conf.setLocking(true) ;// ロックあり.
        Environment env = new Environment(f, conf) ;
        f = null ;
        this.env.set( env ) ;
        this.dirName.set( dir ) ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        if( this.env.get() != null ) {
            this.dirName.set( null ) ;
            Environment env = this.env.get() ;
            this.env.set( null ) ;
            Iterator it = manager.keySet().iterator() ;
            String k ;
            Bdb bdb ;
            try {
                while( it.hasNext() ) {
                    k = ( String )it.next() ;
                    bdb = manager.get( k ) ;
                    if( bdb != null ) {
                        try {
                            bdb.close() ;
                        } catch( Exception ee ) {
                        }
                    }
                }
            } catch( Exception e ) {
            }
            manager.clear() ;
            it = managerq.keySet().iterator() ;
            BQueue queue ;
            try {
                while( it.hasNext() ) {
                    k = ( String )it.next() ;
                    queue = managerq.get( k ) ;
                    if( queue != null ) {
                        queue.close() ;
                    }
                }
            } catch( Exception e ) {
            }
            managerq.clear() ;
            try {
                env.sync() ;
            } catch( Exception e ) {
            }
            try {
                env.cleanLog() ;
            } catch( Exception e ) {
            }
            try {
                env.close() ;
            } catch( Exception e ) {
            }
        }
    }
    
    /**
     * 強制書き込み処理.
     * @exception Exception 例外.
     */
    public void flush() throws Exception {
        Environment env = this.env.get() ;
        if( env == null ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        env.sync() ;
    }
    
    /**
     * Bdbオブジェクトを取得.
     * @param name 対象の名前を設定します.
     * @return Bdb BDBオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public Bdb get( String name ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( env.get() == null ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        Bdb ret = manager.get( name ) ;
        if( ret == null ) {
            ret = new BdbImpl( false,name,this ) ;
        }
        return ret ;
    }
    
    /**
     * BQueueオブジェクトを取得.
     * @param name 対象の名前を設定します.
     * @return BQueue BQueueオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public BQueue getQueue( String name ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( env.get() == null ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        BQueue ret = managerq.get( name ) ;
        if( ret == null ) {
            ret = new BQueueImpl( this,name ) ;
        }
        return ret ;
    }
    
    /**
     * BQueueオブジェクトを取得.
     * <p>情報が存在しない場合のみ、max,warningパラメータは有効になります.
     * 既に存在する場合は、その情報が返されます.</p>
     * @param name 対象の名前を設定します.
     * @return BQueue BQueueオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public BQueue getQueue( String name,long max,double warning ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( env.get() == null ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        BQueue ret = managerq.get( name ) ;
        if( ret == null ) {
            ret = new BQueueImpl( this,name,max,warning ) ;
        }
        return ret ;
    }
    
    /**
     * Bdbオブジェクトを削除.
     * @param name 対象の名前を設定します.
     * @exception Exception 例外.
     */
    protected void remove( String name ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( env.get() == null ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        Bdb b = manager.remove( name ) ;
        if( b != null ) {
            b.close() ;
        }
    }
    
    /**
     * 展開ディレクトリを取得.
     * @return String 展開先のディレクトリ名を取得します.
     */
    public String getDirectory() {
        return dirName.get() ;
    }
    
    /**
     * オブジェクト破棄チェック.
     * @return boolean [true]の場合、破棄されています.
     */
    public boolean isDestroy() {
        return env.get() == null ;
    }
    
    /**
     * 遅延書き込みモードを設定.
     * @param mode [true]の場合、遅延書き込みモードはONになります.
     */
    public static final void setDeferredWrite( boolean mode ) {
        BdbManager._DEFERRED_WRITE = mode ;
    }
    
    /**
     * 遅延書き込みモードを取得.
     * @return boolean [true]の場合、遅延書き込みモードはONになります.
     */
    public static final boolean isDeferredWrite() {
        return BdbManager._DEFERRED_WRITE ;
    }
}

