package org.maachang.session.engine ;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.util.RandomUtil;
import org.maachang.util.thread.LoopThread;

/**
 * セッション生成オブジェクト.
 *  
 * @version 2008/05/25
 * @author  masahito suzuki
 * @since  PersistenceSession 1.00
 */
class CreateSession {
    
    /**
     * 生成セッションタイムアウト値.
     */
    private static final long TIMEOUT = 30000L ;
    
    /**
     * 管理テーブル.
     */
    private Map<String,Long> map = null ;
    
    /**
     * MDBM.
     */
    private SessionCache cacheSession = null ;
    
    /**
     * タイムアウト監視.
     */
    private CreateSessionMonThread thread = null ;
    
    /**
     * コンストラクタ.
     */
    private CreateSession() {
    }
    
    /**
     * コンストラクタ.
     * @param cacheSession 対象のセッションキャッシュを設定します.
     * @exception Exception 例外.
     */
    protected CreateSession( SessionCache cacheSession ) throws Exception {
        if( cacheSession == null || cacheSession.isUse() == false ) {
            throw new IllegalArgumentException( "SessionCacheは不正です" ) ;
        }
        try {
            this.cacheSession = cacheSession ;
            this.map = Collections.synchronizedMap( new HashMap<String,Long>() ) ;
            this.thread = new CreateSessionMonThread( map,TIMEOUT ) ;
        } catch( Exception e ) {
            destroy() ;
            throw e ;
        }
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクトを破棄.
     */
    public synchronized void destroy() {
        if( thread != null ) {
            thread.stopThread() ;
        }
        thread = null ;
        if( map != null ) {
            map.clear() ;
        }
        map = null ;
        cacheSession = null ;
    }
    
    /**
     * 新しいセッションキーを生成.
     * @return String 新しいSessionKeyが返されます.
     * @exception Exception 例外.
     */
    public synchronized String createSessionKey()
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは無効な状態です" ) ;
        }
        String ret = null ;
        for( ;; ) {
            ret = RandomUtil.randomString( PersistenceDefine.SESSION_KEY_LENGTH,true,true,true ) ;
            if( map.containsKey( ret ) == false && cacheSession.containsKey( ret ) == false ) {
                map.put( ret,new Long( System.currentTimeMillis() ) ) ;
                break ;
            }
            ret = null ;
            try {Thread.sleep( 30 ) ;} catch( Exception e ) {}
        }
        return ret ;
    }
    
    /**
     * 指定セッションIDが、存在するかチェック.
     * @param sessionId 対象のセッションIDを設定します.
     * @return boolean [true]の場合、セッションは存在します.
     */
    public synchronized boolean containsKey( String sessionId ) {
        if( sessionId == null ||
            ( sessionId = sessionId.trim() ).length() != PersistenceDefine.SESSION_KEY_LENGTH ) {
            return false ;
        }
        if( map.containsKey( sessionId ) ) {
            map.put( sessionId,new Long( System.currentTimeMillis() ) ) ;
            return true ;
        }
        return false ;
    }
    
    /**
     * オブジェクトが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public synchronized boolean isUse() {
        return ( map == null || cacheSession == null || cacheSession.isUse() == false ||
            thread == null || thread.isStop() == true ) ? false : true ;
    }
}

/**
 * タイムアウト監視スレッド.
 */
class CreateSessionMonThread extends LoopThread {
    private static final Log LOG = LogFactory.getLog( CreateSessionMonThread.class ) ;
    private long timeout = -1L ;
    private Map<String,Long> map = null ;
    
    private CreateSessionMonThread() {
        
    }
    
    public CreateSessionMonThread( Map<String,Long> map,long timeout )
        throws Exception {
        this.map = map ;
        this.timeout = timeout ;
        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 ) ;
                String key = keys.next() ;
                Long time = map.get( key ) ;
                if( time.longValue() + timeout <= System.currentTimeMillis() ) {
                    if( LOG.isDebugEnabled() ) {
                        LOG.debug( "## 生成セッション管理から[" + key + "]を削除" ) ;
                    }
                    keys.remove() ;
                }
            }
        }
        return false ;
    }
    
}

