package org.logical_paradox.rss.rcm;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.logical_paradox.common.cli.Argument;
import org.logical_paradox.common.cli.ArgumentParser;
import org.logical_paradox.rss.ModuleException;
import org.logical_paradox.rss.RSSSubModule;
import org.logical_paradox.rss.dsr.RSSDistributedServiceRegistry;
import org.logical_paradox.rss.lookup.LookupFactory;
import org.logical_paradox.rss.rcm.accessor.ContentsAccessorFactory;

import static org.logical_paradox.rss.rcm.accessor.ContentsAccessorFactory.*;

/**
 * RSSContentsMngrServiceImpl
 * RSS/1b コンテンツ管理システムの実装
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.16 $
 */
public class RSSContentsMngrServiceImpl extends UnicastRemoteObject implements RSSContentsMngrService,RSSSubModule {
	/** シリアルバージョンUID */
	private static final long serialVersionUID = -583832132972585596L;

	/** ロガー */
	private static final Log log = LogFactory.getLog(RSSContentsMngrServiceImpl.class);

	public final static String RCM_CONF_FILENAME = "../conf/rssrcm.conf";	// ログファイル名

	private String rcmServiceName = null;							// このサービスの名称

	private RSSRCMConfig cfg = null;								// コンフィグ
	private int registerServiceCnt = 1;							// 登録したレジスタサービスの通番
	private int finderServiceCnt = 1;								// 登録したファインダーサービスの通番
	private ContentsAccessorFactory accessorFactory = null;		// コンテンツアクセサの生成子
	private ArrayList<RCMContentsRegister> registers = null;		// コンテンツレジスタサービス(キー:サービス名/値:実体)
	private ArrayList<ContentsFinder> finders = null;				// コンテンツファインダーサービス

	private int registerIdx = 0;									// 次に返すべきレジスタサービスの通し番号
	private int finderIdx = 0;										// 次の返すべきファインダーサービスの通し番号

	/** コンフィグファイル名 */
	private final String rcmConfigFilename;

	/**
	 * コンストラクタ．
	 * @throws RemoteException インスタンス化に失敗
	 */
	public RSSContentsMngrServiceImpl() throws RemoteException {
		rcmConfigFilename = RCM_CONF_FILENAME;
	}
	/**
	 * コンストラクタ．
	 * @param filename コンフィグファイル名
	 * @throws RemoteException インスタンス化に失敗
	 */
	public RSSContentsMngrServiceImpl(String filename) throws RemoteException {
		rcmConfigFilename = filename;
	}

	/**
	 * 当該サービスを初期化する
	 */
	protected void init() throws Exception {
		registers = new ArrayList<RCMContentsRegister>();
		finders = new ArrayList<ContentsFinder>();

		log.info("ログライターを初期化しました");

		// コンテンツアクセサのファクトリを生成しておく
		Properties initparam = new Properties();
		initparam.setProperty(PARAM_ACCESSOR_CLASSNAME, cfg.getContentsAccessor());
		initparam.setProperty(PARAM_DSR_URI, cfg.getRSSDistributedServiceRegistry());
		initparam.setProperty(PARAM_INIT_PARAM, cfg.getContentsConnStr());
		initparam.setProperty(PARAM_CONTENTS_ENCODING, cfg.getContentsEncoding());
		accessorFactory = new ContentsAccessorFactory(initparam);

		log.info("リモートコンテンツ管理システムを構成しています");

		// 設定された数のレジスタサービスを事前に作成して，同じRMIレジストリに登録しておく
		for(int i = 0; i < cfg.getInitialRegisterServices(); i++) {
			createRegister();
		}
		// 設定された数のファインダーサービスを事前に作成しておく
		for(int j = 0; j < cfg.getInitialFinderServices(); j++) {
			createFinder();
		}
	}

	/**
	 * コンテンツ検索クライアントを追加する
	 */
	protected void createFinder() throws Exception {
		synchronized(finders) {
			String serviceName = "FINDER_" + (finders.size() + 1);
			finders.add(new ContentsFinderImpl(serviceName, accessorFactory.getAccessor()));

			log.info("ファインダー[" + serviceName + "]を追加しました");
			finderServiceCnt++;
		}
	}

	/**
	 * 新しくコンテンツ登録サービスを追加する
	 * レジスタサービスのクラスは，必ずスタブをexportできるタイプでなければならない
	 * 特に理由がない場合，RCMContentsRegisterImplを使用すること
	 */
	protected void createRegister() throws Exception {
		String serviceName = cfg.getRegisterServicePrefix() + registerServiceCnt;
		RCMContentsRegister r = new RCMContentsRegisterImpl(serviceName, cfg, accessorFactory.getAccessor());
		registers.add(r);

		Naming.rebind(serviceName, r);

		log.info("レジスタサービス[" + serviceName + "]を追加しました");
		registerServiceCnt++;
	}

	/**
	 * 登録されているコンテンツ登録サービスをリスト化する
	 */
	public String[] getRegisterNames() throws RemoteException {
		return null;
	}

	/**
	 * 不要になったコンテンツ登録サービスを削除する
	 */
	public void gc() throws RemoteException {
	}
	/**
	 * コンテンツ検索サービスのインターフェースを取得する
	 */
	public ContentsFinder getFinder() throws RemoteException {
		if(finderIdx >= finders.size()) {
			finderIdx = 0;
		}
		ContentsFinder finder = (ContentsFinder)finders.get(finderIdx++);
		log.info("ファインダーサービス[" + finder.getName() + "が選択されました");
		return finder;
	}
	/**
	 * コンテンツ登録サービスのインターフェースを取得する
	 */
	public ContentsRegister getRegister() throws RemoteException {
		if(registerIdx >= registers.size()) {
			registerIdx = 0;
		}

		ContentsRegister r = (ContentsRegister)registers.get(registerIdx++);
		log.info("レジスタサービス[" + r.getRegisterName() + "]が選択されました");
		return r;
	}

	/**
	 * サービス登録用メソッド
	 */
	public static void main(String[] args) throws Exception {
		String filename = null;
		for(Argument arg : ArgumentParser.parse(args)) {
			if(arg.isOption()) {
				if("config".equals(arg.getName())) {
					filename = arg.getValue();
				}
			}
		}
		// コンテンツ管理システムのインスタンスを生成する
		RSSContentsMngrServiceImpl server = null;
		if(filename == null) {
			server = new RSSContentsMngrServiceImpl();
		} else {
			server = new RSSContentsMngrServiceImpl(filename);
		}
		server.startModule();
	}

	/**
	 * モジュールコントロール用インターフェース::
	 * モジュールを開始する
	 */
	public void startModule() throws ModuleException {
		try {
			cfg = new RSSRCMConfig(rcmConfigFilename);
			// コンテンツ管理システムをリモートサービス選択サーバーへ登録する
			// 登録時，このサービスの名前も頂戴する．
			rcmServiceName = LookupFactory.getLookup(cfg.getRSSDistributedServiceRegistry()).bind(RSSDistributedServiceRegistry.RSS_SERVID_REMOTE_CONTENTS_MGR, this);
			init();
		} catch(Exception e) {
			throw new ModuleException(e);
		}

		log.info("リモートコンテンツ管理システムを開始しました");
	}

	/**
	 * モジュールコントロール用インターフェース::
	 * モジュールを一時停止する
	 */
	public void suspendModule() throws ModuleException {
	}

	/**
	 * モジュールコントロール用インターフェース::
	 * 停止しているモジュールの実行を再開する
	 */
	public void continueModule() throws ModuleException {
	}

	/**
	 * モジュールコントロール用インターフェース::
	 * モジュールを停止する
 	 * このメソッドが呼ばれた場合，モジュールは安全な方法で終了する
	 * 終了したモジュールは，新しくインスタンスが生成されないと開始することはできない
	 */
	public void stopModule() throws ModuleException {
	}

	/**
	 * サービス名を返す
	 * @return サービス名
	 * @throws RemoteException サービス名取得に失敗
	 */
	public String getName() throws RemoteException {
		return rcmServiceName;
	}
	/**
	 * 完全修飾サービス名を返す
	 * @return 完全修飾サービス名
	 * @throws RemoteException サービス名取得に失敗
	 */
	public String getFqsn() throws RemoteException {
		return rcmServiceName + "@" + cfg.getRSSDistributedServiceRegistry();
	}
	/**
	 * サービスを停止する
	 * RMIサーバーとして実装されている場合，unexportされる
	 * @param force true:強制停止 / false:安全に停止
	 * @throws RemoteException サービス停止に失敗
	 */
	public void shutdown(boolean force) throws RemoteException {
		log.info("サーバーを停止します");

		log.info("コンテンツ登録サービスを停止しています");
		synchronized(registers) {
			for(Iterator it = registers.iterator(); it.hasNext();) {
				ContentsRegister cr = (ContentsRegister)it.next();
				cr.shutdown();
				if(cr instanceof RCMContentsRegister) {
					UnicastRemoteObject.unexportObject((RCMContentsRegister)cr, force);
				}
			}
			registers.clear();
		}
		log.info("コンテンツ検索サービスを停止しています");
		synchronized(finders) {
			for(Iterator it = finders.iterator(); it.hasNext();) {
				ContentsFinder cf = (ContentsFinder)it.next();
				UnicastRemoteObject.unexportObject(cf, force);
			}
			finders.clear();
		}

		// DSRから切り離す
		try {
			log.info("リモートコンテンツ管理システムをDSRから切り離しています");
			LookupFactory.getLookup(cfg.getRSSDistributedServiceRegistry()).unbind(RSSDistributedServiceRegistry.RSS_SERVID_REMOTE_CONTENTS_MGR, rcmServiceName);
		} catch (Exception e1) {
			throw new RemoteException(e1.getMessage());
		}


		UnicastRemoteObject.unexportObject(this, force);

		log.info("サーバーを停止しました");
	}
}

// end of RSSContentsMngrServiceImpl.java
