/**
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.common.session;

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

import org.logical_paradox.common.util.StringUtils;

/**
 * セッションマネージャー
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public class SessionManager {
	/** セッションIDの長さ */
	public static final int SESSION_ID_LENGTH = 32;
	/** セッション監視間隔(msec) */
	public static final int DEFAULT_SESSION_CHECK_INTERVAL = 1000;

	/** セッション */
	public final HashMap sessions = new HashMap();
	/** セッション用GC */
	private final SessionUnbounder unbounder;
	/** セッションの生存期間 */
	private long sessionLifeTime;

	/**
	 * コンストラクタ
	 */
	public SessionManager() {
		this(DEFAULT_SESSION_CHECK_INTERVAL);
	}
	/**
	 * コンストラクタ
	 * @param itv セッション監視間隔
	 */
	public SessionManager(int itv) {
		unbounder = new SessionUnbounder(this, itv);
		unbounder.start();
	}
	/**
	 * コンストラクタ
	 * @param itv セッション監視間隔
	 * @param lifetime セッションの有効期限(msec)
	 */
	public SessionManager(int itv, long lifetime) {
		this(itv);
		sessionLifeTime = lifetime;
	}
	/**
	 * 新しいセッションを開始する
	 * @return 新しく追加されたセッション
	 */
	public Session getSession() throws IllegalSessionStateException {
		String sessionId = null;

		// 同じセッションIDが使用されないようにする
		while(true) {
			sessionId = getSessionId();
			if(sessions.get(sessionId) == null) {
				break;
			}
		}

		// 新しくセッションを追加する
		Session session = null;
		if(sessionLifeTime == 0) {
			session = new Session(sessionId);
		} else {
			session = new Session(sessionId, sessionLifeTime);
		}
		sessions.put(sessionId, session);
		
		return session;
	}
	/**
	 * 新しいセッションを開始する
	 * 指定されたセッションIDが存在する場合、そのセッションを継続する
	 * 存在しない場合、新しいセッションを返す。その場合、セッションIDは指定されたものとは限らない
	 * @param sessionId 継続要求をするセッションID
	 * @return 新しく作成された、または継続されたセッションID
	 */
	public Session getSession(String sessionId) throws IllegalSessionStateException {
		synchronized(sessions) {
			Session session = (Session)sessions.get(sessionId);
			if(session == null) {
				// 既存のセッションが存在しないため、新しいセッションを作成する
				return getSession();
			} else {
				return session;
			}
		}
	}
	/**
	 * 新しいセッションを開始する
	 * createModeがtrueの場合、指定されたセッションIDが存在する場合、そのセッションを継続する
	 * 存在しない場合、新しいセッションを返す。その場合、セッションIDは指定されたものとは限らない
	 * createModeがfalseの場合、既存のセッションが存在しない場合はnullを返す
	 * @param sessionId 継続要求をするセッションID
	 * @return 新しく作成された、または継続されたセッションID
	 */
	public Session getSession(String sessionId, boolean createMode) throws IllegalSessionStateException {
		if(createMode == true) {
			return getSession(sessionId);
		} else {
			synchronized(sessions) {
				return (Session)sessions.get(sessionId);
			}
		}
	}
	/**
	 * 指定されたセッションが存在するかどうかを返す
	 * @return true:存在する / false:存在しない(タイムアウトしたなど)
	 */
	public synchronized boolean isExists(String sessionId) {
		return sessions.get(sessionId) != null;
	}
	/**
	 * セッションを削除する
	 * セッションを正しくクローズし，セッションマネージャーの管理から解放する
	 * @param sessionId セッションID
	 */
	public synchronized void removeSession(String sessionId) {
		Session session = (Session)sessions.get(sessionId);
		if(session == null) {
			return;
		}

		session.unbound();
		sessions.remove(sessionId);
	}
	/**
	 * 全てのセッションを削除する
	 * セッションを正しくクローズし，セッションマネージャーの管理から解放する
	 * @param sessions 閉じるセッションのリスト
	 */
	public void removeAllSessions(List sessions) {
		for(Iterator it = sessions.iterator(); it.hasNext();) {
			Session session = (Session)it.next();
			removeSession(session.getSessionId());
		}
	}
	/**
	 * 全てのセッションを返す
	 * @return 全セッション
	 */
	synchronized Map getAllSessions() {
		return sessions;
	}
	/**
	 * セッションIDを返す
	 * @return セッションID
	 */
	private String getSessionId() {
		return StringUtils.randomText(SESSION_ID_LENGTH, true);
	}
}
