/*
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 * $Id: DomainControllerImpl.java,v 1.4 2005/09/28 16:14:44 rampil Exp $
 */
package org.logical_paradox.vsan.domain;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.logical_paradox.vsan.node.DiskArrayNode;

/**
 * ドメインコントローラーの実装
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.4 $
 */
public class DomainControllerImpl extends UnicastRemoteObject implements DomainController {
	/** ロガー */
	private static final Log log = LogFactory.getLog(DomainControllerImpl.class);
	/** ドメインのマップ */
	private final HashMap domains = new HashMap();
	/** ドメインコントローラーの識別名 */
	private final String name;

	/**
	 * コンストラクタ
	 * @param nodeId 識別名
	 * @throws RemoteException インスタンスの生成に失敗した
	 */
	public DomainControllerImpl(String nodeId) throws RemoteException {
		name = nodeId;
	}
	/**
	 * ドメインコントローラーの識別名を返す
	 * @return
	 */
	public String getName() {
		return name;
	}
	/**
	 * 登録されているドメイン名を返す．
	 * @return ドメイン名
	 * @throws RemoteException 情報の取得に失敗
	 */
	public String[] getDomainNames() throws RemoteException {
		synchronized(domains) {
			String[] dn = (String[])domains.keySet().toArray(new String[0]);
			return dn;
		}
	}
	/**
	 * ドメイン情報を取得する
	 * @param name ドメイン名
	 * @return ドメイン情報
	 * @throws RemoteException 情報の取得に失敗
	 */
	public Domain getDomain(String name) throws RemoteException {
		Domain domain = (Domain)domains.get(name);
		if(domain == null) {
			throw new RemoteException();
		}

		return domain;
	}
	/**
	 * 指定ドメインにノードを追加する．
	 * 追加しようとするノードが既にある場合，ノード情報を更新する．
	 * @param domainname ドメイン名
	 * @param node ノード情報
	 * @throws RemoteException 追加に失敗
	 */
	public void join(String domainname, DiskArrayNode node) throws RemoteException {
		Domain domain = (Domain)domains.get(domainname);
		if(domain == null) {
			domain = new Domain(domainname, new DiskArrayNode[]{});
		}

		// ドメインに対してノードを追加する
		if(domain.getRecordSize() >= 0 && node.getRecordSize() > 0 && domain.getRecordSize() != node.getRecordSize()) {
			log.error("レコードサイズが異なるため、ドメインにDACを追加することができませんでした。");
			throw new RemoteException();
		}

		// 既にあるノードを一旦削除してから追加(=置換)
		domain.remove(node.getHost());
		domain.add(node);

		domains.put(domainname, domain);

		log.info("ドメイン[" + domainname + "]に、ノード[" + node.getHost() + "]を追加しました");
	}
	/**
	 * 指定ドメインから離脱する
	 * @param host ノードのホストアドレス
	 * @param node ノード情報
	 * @throws RemoteException 離脱に失敗
	 */
	public void leave(String domainname, String host) throws RemoteException {
		Domain domain = (Domain)domains.get(domainname);
		if(domain == null) {
			// ドメイン自体が存在しないので無視する
			return;
		}
		domain.remove(host);
		if(domain.isEmpty() == false) {
			domains.put(domainname, domain);
		} else {
			// 1つもノードがなくなってしまった場合，ドメイン情報を削除する
			domains.remove(domainname);
		}

		log.info("ドメイン[" + domainname + "]から、ノード[" + host + "]を削除しました");
	}

	/**
	 * ドメインコントローラーのブートストラップ
	 * @param args プログラム引数
	 * @throws Exception 処理されなかった例外
	 */
	public static final void main(String[] args) throws Exception {
		if(args == null || args.length == 0) {
			System.err.println("usage: java DomainControllerImpl <node-id>");
			return;
		}

		// ドメインコントローラーを開始する
		DomainControllerImpl dci = new DomainControllerImpl(args[0]);
		Naming.rebind(dci.getName(), dci);

		log.info("ドメインコントローラー[" +  dci.getName() + "]を開始しました。");
	}
}
