/*
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 * $Id: DomainConnectionProvider.java,v 1.2 2005/09/07 13:38:06 rampil Exp $
 */
package org.logical_paradox.vsan.dfs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.logical_paradox.vsan.domain.*;
import org.logical_paradox.vsan.domain.exception.DomainAccessException;

/**
 * ドメイン接続プロバイダー
 * 状況によって、新しいドメイン接続を返す場合もあるし、既存のものをそのまま返す場合もある
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.2 $
 */
public class DomainConnectionProvider {
	/** コンフィグレーション */
	private final DfsConfig config;
	/** 接続先ドメイン情報 */
	private Domain domain;

	/** 接続プール */
	private List connectionPool = new ArrayList();

	/**
	 * コンストラクタ．
	 * @param cfg DFSのコンフィグ
	 * @param d 接続先ドメイン情報
	 */
	public DomainConnectionProvider(DfsConfig cfg, Domain d) {
		config = cfg;
		domain = d;
	}
	/**
	 * 接続先ドメイン情報を設定する．
	 * @param d 接続先ドメイン情報
	 */
	public void setDomain(Domain d) {
		domain = d;
	}
	/**
	 * 接続先ドメイン情報を返す．
	 * @return 接続先ドメイン情報
	 */
	public Domain getDomain() {
		return domain;
	}
	/**
	 * 現在の接続プーリング数を返す．
	 * @return 接続プーリング数
	 */
	protected int size() {
		return connectionPool.size();
	}
	/**
	 * ドメイン接続を取得する
	 * ドメイン接続は、適当な数のクライアントで共有される<br>
	 * 同じクライアントに対して、常に同じ接続インスタンスが割り当てられるため、<br>
	 * 同じ引数で何回呼んでも戻り値が同じになる。<br>
	 * 割り当てに失敗した場合、DomainAccessExceptionを返す
	 * @param clientNodeId クライアントID(nullの場合、未割り当て)
	 * @return 指定されたクライアントに割り当てられたドメイン接続
	 * @throws DomainAccessException 割り当てに失敗
	 */
	public DomainConnection getConnection(String clientNodeId) throws DomainAccessException {
		synchronized(connectionPool) {
			// コネクション数が規定の数より少ない場合、問答無用で新しい接続を確立して
			// それを返す
			if(connectionPool.size() < config.getDomainConnectionMax()) {
				DomainConnection con = newConnection(clientNodeId);
				connectionPool.add(con);
				return con;
			}
			for(Iterator it = connectionPool.iterator(); it.hasNext();) {
				DomainConnection dc = (DomainConnection)it.next();
				if(dc.contains(clientNodeId) == true) {
					return dc;
				}
			}

			// 未割り当てなので、共有数の少ない順にソートする
			Collections.sort(connectionPool);

			// 新しい接続を割り当てるか、流用する
			DomainConnection con = (DomainConnection)connectionPool.get(0);
			if(con.getSharedNum() >= config.getDomainSessionConnections()) {
				// 接続数が多すぎる
				throw new DomainAccessException();
			}

			// 接続共有する
			con.share(clientNodeId);

			return con;
		}
	}
	/**
	 * 新しいドメイン接続を確立する．
	 * @param clientNodeId クライアントノードID
	 * @return 生成されたドメイン接続
	 */
	protected DomainConnection newConnection(String clientNodeId) throws DomainAccessException {
		return new DomainConnection(clientNodeId, domain);
	}
}
