/*
 * $Id: SiteLockMgr.java,v 1.2 2005/10/22 16:27:27 rampil Exp $
 * Copyright (c) 2005 LOGICAL-PARADOX.ORG
 */
package org.logical_paradox.rss.router;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * SiteLockMgr
 * サイトロック状況を管理するクラス
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.2 $
 */
public class SiteLockMgr {
	/** 5分以上アクセスの内ロックオブジェクトは解放 */
	public static final int SITELOCK_EXPIRE = 1000 * 60 * 5;

	/** ロック */
	private Hashtable locks = new Hashtable();
	/** サイトロックの有効期限 */
	private long siteLockExpire;

	/**
	 * コンストラクタ．
	 */
	public SiteLockMgr() {
		siteLockExpire = SITELOCK_EXPIRE;
	}
	/**
	 * コンストラクタ．
	 * @param expire サイトロックの有効期限(msec)
	 */
	public SiteLockMgr(long expire) {
		siteLockExpire = expire;
	}

	/**
	 * ロック情報を追加する
	 * ロックを追加することができた場合はtrue，失敗した場合はfalseが返る
	 */
	public boolean lock(SiteLock l) {
		boolean rc = true;

		synchronized(locks) {
			SiteLock o = (SiteLock)locks.get(getSiteKey(l));
			if(o != null) {
				rc = false;
			} else {
				locks.put(getSiteKey(l), l);
				rc = true;
			}
		}

		return rc;
	}

	/**
	 * ロックを解除する
	 */
	public void unlock(SiteLock l) {
		synchronized(locks) {
			SiteLock o = (SiteLock)locks.get(getSiteKey(l));
			if(o == null) {
				return;
			}
			locks.remove(getSiteKey(l));
		}
	}

	/**
	 * 指定サイトをロックしているロボットのIDを返す
	 * 誰もロックしていない場合はnullが返る
	 */
	public String getLockOwnerId(SiteLock l) {
		String owner = null;
		return owner;
	}

	/**
	 * 指定IDの探索ノードが保有しているロックの数を返す
	 */
	public int numOfMyLocks(String robotId) {
		Hashtable p = null;
		synchronized(locks) {
			p = (Hashtable)locks.clone();
		}

		Enumeration e = p.elements();
		int num = 0;

		while(e.hasMoreElements()) {
			SiteLock i = (SiteLock)e.nextElement();
			if(i.getRobotId().equals(robotId)) {
				num++;
			}
		}

		p = null;

		return num;
	}

	/**
	 * 指定されたIDのロボットがロックしているサイトを返す
	 * 何もロックしていない場合はnullが返る
	 */
	public String[] getLockingSitesByOwner(String id) {
		Vector ar = new Vector();
		Hashtable p = null;

		synchronized(locks) {
			p = (Hashtable)locks.clone();
		}

		Enumeration e = p.elements();
		while(e.hasMoreElements()) {
			SiteLock l = (SiteLock)e.nextElement();
			if(l.getRobotId().equals(id)) {
				ar.addElement(l);
			}
		}

		return (String[])ar.toArray(new String[1]);
	}

	/**
	 * 指定されたロックを取得する
	 * 指定され探索ノードのIDで取得できなかった場合はnull
	 */
	public SiteLock findLock(String dn, String rid) {
		String skey = new String(dn);
		SiteLock l = null;

		synchronized(locks) {
			l = (SiteLock)locks.get(skey);
		}

		if(l != null && l.getRobotId().equals(rid)) {
			// ロックオブジェクトの日付を更新
			// 検索されたということは，誰かが必要としているということなので．
			l.touch();

			// このサイトのロックの保持者は自分だった
			return l;
		} else {
			// 自分以外がロックしているか，あるいはロック自体が存在しない場合
			return null;
		}
	}

	/**
	 * サイトロック管理用のキーを生成する
	 */
	protected String getSiteKey(SiteLock l) {
		return l.getSiteName();
	}

	/**
	 * ロックのキーを返す
	 * ここでいうキーとは，ドメイン名のこと
	 */
	public String[] getSiteNames() {
		Vector v = new Vector();
		synchronized(locks) {
			Enumeration e = locks.keys();
			while(e.hasMoreElements()) {
				v.addElement(e.nextElement());
			}
		}

		return (String[])v.toArray(new String[1]);
	}

	/**
	 * 指定された探索ノードが保有する全てのロックを解放する
	 */
	public void releaseAll(String robotId) {
		synchronized(locks) {
			Enumeration e = locks.elements();
			while(e.hasMoreElements()) {
				SiteLock l = (SiteLock)e.nextElement();
				if(l.getRobotId().equals(robotId)) {
					// 指定された探索ノードIDの保有するロックの場合は削除フラグを立てる
					// 実際には，スレッドが適当なタイミングで除去する
					l.markGC();
				}
			}
		}
	}

	/**
	 * サイトロックのゴミを回収する
	 * 回収したゴミのリストを配列で返す
	 */
	public SiteLock[] gc() {
		Vector removedLockObjs = new Vector();

		synchronized(locks) {
			String[] keys = getSiteNames();
			for(int i = 0; keys != null && i < keys.length; i++) {
				if(keys[i] == null) {
					continue;
				}

				SiteLock l = (SiteLock)locks.get(keys[i]);
				if(l != null) {
					// しばらくアクセスされなかったようであれば，ロックオブジェクトを解放可能状態に設定する
					if(siteLockExpire > 0 && Calendar.getInstance().getTimeInMillis() - l.getLastAccessTime() >= siteLockExpire) {
						l.markGC();
					}
				}
				if(l != null && l.isBroken() == true) {
					// 削除する
					removedLockObjs.addElement(locks.remove(keys[i]));
				}
			}
		}

		return (SiteLock[])removedLockObjs.toArray(new SiteLock[0]);
	}
}

// end of SiteLockMgr.java
