package org.maachang.session.client.net ;

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

import org.maachang.session.engine.PersistenceDefine;
import org.maachang.util.thread.LoopThread;

/**
 * クライアントで、問合せセッションIDを一時保管.
 * 
 * @version 2008/05/25
 * @author masahito suzuki
 * @since PersistenceSession 1.01
 */
public class ClientSessionCache {
    
    /**
     * タイムアウト値.
     */
    private static final int TIMEOUT = 3500 ;
    
    /**
     * 管理テーブル.
     */
    private Map<String,Long> map = null ;
    
    /**
     * 削除監視スレッド.
     */
    private ClientSessionCacheThread thread = null ;
    
    /**
     * 同期オブジェクト.
     */
    private final Object sync = new Object() ;
    
    /**
     * コンストラクタ.
     * @exception Exception 例外.
     */
    protected ClientSessionCache() throws Exception {
        try {
            this.map = Collections.synchronizedMap( new HashMap<String,Long>() ) ;
            this.thread = new ClientSessionCacheThread( sync,map,TIMEOUT ) ;
        } catch( Exception e ) {
            destroy() ;
            throw e ;
        }
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクトを破棄.
     */
    public void destroy() {
        synchronized( sync ) {
            if( thread != null ) {
                thread.stopThread() ;
            }
            thread = null ;
            if( map != null ) {
                map.clear() ;
            }
            map = null ;
        }
    }
    
    /**
     * キャッシュ化するセッションIDを登録.
     * @param sessionId 対象のセッションIDを設定します.
     */
    public void put( String sessionId ) {
        synchronized( sync ) {
            if( isUse() ) {
                if( sessionId == null ||
                    ( sessionId = sessionId.trim() ).length() != PersistenceDefine.SESSION_KEY_LENGTH ) {
                    return ;
                }
                map.put( sessionId,new Long( System.currentTimeMillis() ) ) ;
            }
        }
    }
    
    /**
     * 指定セッションIDを削除.
     * @param sessionId 対象のセッションIDを設定します.
     */
    public void remove( String sessionId ) {
        synchronized( sync ) {
            if( isUse() == false ) {
                return ;
            }
            if( sessionId == null ||
                ( sessionId = sessionId.trim() ).length() != PersistenceDefine.SESSION_KEY_LENGTH ) {
                return ;
            }
            if( map.containsKey( sessionId ) ) {
                map.remove( sessionId ) ;
            }
        }
    }
    
    /**
     * 指定セッションIDが、存在するかチェック.
     * @param sessionId 対象のセッションIDを設定します.
     * @return boolean [true]の場合、セッションは存在します.
     */
    public boolean containsKey( String sessionId ) {
        synchronized( sync ) {
            if( isUse() == false ) {
                return false ;
            }
            if( sessionId == null ||
                ( sessionId = sessionId.trim() ).length() != PersistenceDefine.SESSION_KEY_LENGTH ) {
                return false ;
            }
            Long time = map.get( sessionId ) ;
            if( time != null ) {
                if( time.longValue() + TIMEOUT <= System.currentTimeMillis() ) {
                    map.remove( sessionId ) ;
                }
                else {
                    map.put( sessionId,new Long( System.currentTimeMillis() ) ) ;
                    return true ;
                }
            }
            return false ;
        }
    }
    
    /**
     * オブジェクトが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public boolean isUse() {
        synchronized( sync ) {
            return ( map == null || thread == null || thread.isStop() == true ) ? false : true ;
        }
    }
    
}

/**
 * タイムアウト監視スレッド.
 */
class ClientSessionCacheThread extends LoopThread {
    private long timeout = -1L ;
    private Map<String,Long> map = null ;
    private Object sync = null ;
    
    private ClientSessionCacheThread() {
        
    }
    
    public ClientSessionCacheThread( Object sync,Map<String,Long> map,long timeout )
        throws Exception {
        this.map = map ;
        this.timeout = timeout ;
        this.sync = sync ;
        startThread() ;
    }
    
    protected void clear() {
        this.map = null ;
    }
    
    protected boolean execution() throws Exception {
        if( map.size() <= 0 ) {
            Thread.sleep( 500 ) ;
        }
        else {
            for( Iterator<String> keys = map.keySet().iterator() ; keys.hasNext() ; ) {
                Thread.sleep( 50 ) ;
                synchronized( sync ) {
                    String key = keys.next() ;
                    if( key == null ) {
                        continue ;
                    }
                    Long time = map.get( key ) ;
                    if( time.longValue() + timeout <= System.currentTimeMillis() ) {
                        keys.remove() ;
                    }
                }
            }
        }
        return false ;
    }
    
}

