package org.maachang.comet.httpd.engine.script.dao;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.maachang.util.thread.LoopThread;

/**
 * マスターキャッシュオブジェクト.
 * 
 * @version 2008/07/14
 * @author masahito suzuki
 * @since MaachangComet 1.1F
 */
public class MasterCache {
    
    /**
     * デフォルトキャッシュ破棄時間.
     */
    private static final long DEF_DESTROY_TIME = 300000L ;
    
    /**
     * 最小キャッシュ破棄時間.
     */
    private static final long MIN_DESTROY_TIME = 30000L ;
    
    /**
     * 最大キャッシュ破棄時間.
     */
    private static final long MAX_DESTROY_TIME = 2678400000L ;
    
    /**
     * キャッシュ格納領域.
     */
    private Map<String,Object> cache = null ;
    
    /**
     * キャッシュ監視オブジェクト.
     */
    private MasterCacheMonitor mon = null ;
    
    /**
     * コンストラクタ.
     * @exception Exception 例外.
     */
    public MasterCache() throws Exception {
        this( DEF_DESTROY_TIME ) ;
    }
    
    /**
     * コンストラクタ.
     * @param time キャッシュ破棄時間を設定します.
     * @exception Exception 例外.
     */
    public MasterCache( long time ) throws Exception {
        if( time <= -1 ) {
            time = MAX_DESTROY_TIME ;
        }
        else if( time <= MIN_DESTROY_TIME ) {
            time = MIN_DESTROY_TIME ;
        }
        else if( time >= MAX_DESTROY_TIME ) {
            time = MAX_DESTROY_TIME ;
        }
        this.cache = new HashMap<String,Object>() ;
        this.mon = new MasterCacheMonitor( cache,time ) ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public synchronized void destroy() {
        if( mon != null ) {
            mon.destroy() ;
        }
        if( cache != null ) {
            cache.clear() ;
        }
        mon = null ;
        cache = null ;
    }
    
    /**
     * キャッシュ監視を行わない情報を追加.
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     */
    public synchronized void put( String key,Object value ) {
        if( key == null || ( key = key.trim() ).length() <= 0 || value == null ) {
            return ;
        }
        cache.put( key,value ) ;
    }
    
    /**
     * キャッシュ監視を行う情報を追加.
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     */
    public synchronized void putByCache( String key,Object value ) {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            return ;
        }
        cache.put( key,new MasterCacheChild( value ) ) ;
    }
    
    /**
     * キャッシュ内容をクリア.
     * @param key 対象のキー名を設定します.
     */
    public synchronized void clearCache( String key ) {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            return ;
        }
        Object o = cache.get( key ) ;
        if( o instanceof MasterCacheChild ) {
            ( ( MasterCacheChild )o ).clearValue() ;
        }
    }
    
    /**
     * 情報を削除.
     * @param key 対象のキー名を設定します.
     */
    public synchronized void remove( String key ) {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            return ;
        }
        cache.remove( key ) ;
    }
    
    /**
     * 情報を取得.
     * @param key 対象のキー名を設定します.
     */
    public synchronized Object get( String key ) {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            return null ;
        }
        return cache.get( key ) ;
    }
    
    /**
     * 情報が存在するかチェック.
     * @param key 対象のキー名を設定します.
     * @return boolean [true]の場合、キー名の内容が存在します.
     */
    public synchronized boolean containsKey( String key ) {
        return cache.containsKey( key ) ;
    }
    
    /**
     * Mapオブジェクトを取得.
     */
    public synchronized Map getMap() {
        return cache ;
    }
}

/**
 * マスターキャッシュ要素.
 */
class MasterCacheChild {
    private Object value = null ;
    private long lastAccessTime = -1L ;
    
    public MasterCacheChild() {
        
    }
    
    public MasterCacheChild( Object value ) {
        if( value == null ) {
            this.value = null ;
            this.lastAccessTime = -1L ;
        }
        else {
            this.value = value ;
            this.lastAccessTime = System.currentTimeMillis() ;
        }
    }
    
    public synchronized void clearValue() {
        value = null ;
        lastAccessTime = -1L ;
    }
    
    public synchronized void setValue( Object value ) {
        if( value == null ) {
            this.value = null ;
            this.lastAccessTime = -1L ;
        }
        else {
            this.value = value ;
            this.lastAccessTime = System.currentTimeMillis() ;
        }
    }
    
    public synchronized Object getValue() {
        lastAccessTime = System.currentTimeMillis() ;
        return value ;
    }
    
    public synchronized long getLastAccessTime() {
        return lastAccessTime ;
    }
    
    public synchronized boolean isNoValue() {
        return ( value == null ) ? true : false ;
    }
}

/**
 * キャッシュ監視スレッド.
 */
class MasterCacheMonitor extends LoopThread {
    private long time = -1L ;
    private Map<String,Object> cache = null ;
    protected MasterCacheMonitor( Map<String,Object> cache,long time )
        throws Exception {
        this.time = time ;
        this.cache = cache ;
        this.startThread() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        super.stopThread() ;
    }
    
    /**
     * オブジェクトクリア.
     */
    protected void clear() {
        cache = null ;
        time = -1 ;
    }
    
    /**
     * 実行処理.
     * @exception Exception 例外.
     */
    protected boolean execution() throws Exception {
        if( cache.size() <= 0 ) {
            return false ;
        }
        Iterator<String> it = cache.keySet().iterator() ;
        while( it.hasNext() ) {
            if( super.isStop() == true ) {
                break ;
            }
            String key = it.next() ;
            Object o = cache.get( key ) ;
            if( o == null || ( o instanceof MasterCacheChild ) == false ) {
                Thread.sleep( 30L ) ;
                continue ;
            }
            MasterCacheChild ch = ( MasterCacheChild )o ;
            if( ch.getLastAccessTime() != -1 &&
                ch.getLastAccessTime() + time <= System.currentTimeMillis() ) {
                ch.clearValue() ;
            }
            Thread.sleep( 30L ) ;
        }
        return true ;
    }
}
