/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.report2;

import java.io.File;
import java.io.IOException;

import org.opengion.fukurou.system.ThrowUtil;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.util.AbstractObjectPool;
import org.opengion.fukurou.util.Cleanable;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.SystemManager;

/**
 * Sofficeのﾌﾟﾛｾｽを管理するﾌｧｸﾄﾘｸﾗｽです｡
 * ﾌﾟﾛｾｽﾌﾟｰﾙの実装は､AbstractObjectPoolを継承して実装されています｡
 *
 * ﾌﾟﾛｾｽの初期生成数は0です｡最大生成数は､ｼｽﾃﾑﾘｿｰｽのREPORT_MAX_PROCESS_COUNTで
 * 定義されます｡また､生存時間は､REPORT_PROCESS_ALIVEで定義されています｡
 *
 * ﾌﾟﾛｾｽを全て終了するには､clearﾒｿｯﾄﾞを呼び出します｡
 * clearﾒｿｯﾄﾞは､Cleanableｲﾝﾀｰﾌｪｰｽの実装として組み込まれ､SytemManagerに登録されるため､
 * Tomcat終了時に､自動的にﾌﾟﾛｾｽが終了されます｡
 * 但し､貸し出し中(処理中)のﾌﾟﾛｾｽは､AbstractObjecgPoolの実装から､終了されないため､別の方法で
 * 明示的にkillする必要があります
 *
 * @version  4.0
 * @author   Hiroki Nakamura
 * @since    JDK5.0,
 */
public final class ProcessFactory {

	/**
	 * ﾌﾟﾛｾｽﾌﾟｰﾙ
	 */
	private static ProcessPool pp = new ProcessPool() ;

	/** Cleanable ｲﾝﾀｰﾌｪｰｽによる初期化処理 */
	static {
		final Cleanable clr = new Cleanable() {
			public void clear() {
				ProcessFactory.clear();
			}
		};
		SystemManager.addCleanable( clr );

		// 5.2.2.0 (2010/11/01) 循環参照解消のため､SystemManager から移動
		final Cleanable clr2 = new Cleanable() {
			/**
			 * 初期化(ｸﾘｱ)します｡
			 * 主に､ｷｬｯｼｭｸﾘｱで利用します｡
			 */
			public void clear() {
				ProcessFactory.kill();
			}
		};
		SystemManager.addCleanable( clr2 , true );	// ｺﾝﾃｷｽﾄ終了時のみ呼び出す
	}

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰをprivateにして､
	 * ｵﾌﾞｼﾞｪｸﾄの生成をさせないようにする｡
	 */
	private ProcessFactory() {
	}

	/**
	 * OpenOfficeのﾌﾟﾛｾｽを生成します｡
	 *
	 * @return	sofficeのﾌﾟﾛｾｽ
	 */
	public static SOfficeProcess newInstance() {
		return pp.newInstance();
	}

	/**
	 * OpenOfficeのﾌﾟﾛｾｽをﾘﾘｰｽします｡
	 *
	 * @param  soffice SOfficeProcessｵﾌﾞｼﾞｪｸﾄ
	 */
	public static void release( final SOfficeProcess soffice ) {
		pp.release( soffice );
	}

	/**
	 * OpenOfficeのﾌﾟﾛｾｽをｸﾛｰｽﾞします｡
	 *
	 * @param   soffice SOfficeProcessｵﾌﾞｼﾞｪｸﾄ
	 */
	public static void remove( final SOfficeProcess soffice ) {
		pp.remove( soffice );
	}

	/**
	 * ﾌﾟｰﾙされているOpenOfficeのﾌﾟﾛｾｽを全てｸﾛｰｽﾞします｡
	 */
	public static void clear() {
		pp.clear();
	}

	/**
	 * 全てのsoffice.binﾌﾟﾛｾｽをKILLします｡
	 * ｱﾌﾟﾘｹｰｼｮﾝの終了処理で実行します｡
	 * OS名がWindowsを含む場合はtaskkill､それ以外の場合はkillallします｡
	 * 又､ﾌﾟﾛｾｽ終了後にｺﾋﾟｰされた設定ﾌｧｲﾙを削除します｡
	 *
	 * @og.rev 4.3.0.0 (2008/07/18) 追加
	 * @og.rev 4.3.0.0 (2008/07/22) 設定ﾌｧｲﾙの削除を追加
	 * @og.rev 4.3.5.0 (2009/02/01) Exception をそれぞれのExceptionに分けて捕らえる｡
	 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を､ThrowUtil#ogStackTrace(Throwable) に置き換え｡
	 */
	public static void kill() {
		try {
			final String osName = HybsSystem.sys( "OS_INFO" ); //System.getProperty("os.name");
			if( osName.indexOf( "Windows" ) >= 0 ){
				// 4.3.0.0 (2008/07/18) Windoesのtaskkillを利用してsoffice.binのﾀｽｸを強制終了します｡
				new ProcessBuilder( "cmd.exe","/c","taskkill","/F","/IM","soffice.bin" ).start().waitFor();
			}
			else{
				// 4.3.0.0 (2008/07/24) Windowsではない場合はkillallｺﾏﾝﾄﾞ
				new ProcessBuilder( "killall","-9","-w","soffice.bin" ).start().waitFor();
			}

			// 4.3.0.0 (2008/07/22) 設定ﾌｧｲﾙ(SOfficeProcessでﾃﾞｨﾚｸﾄﾘを設定)を全削除します｡
			FileUtil.deleteFiles( new File( SOfficeProcess.ENV_DIR ) );
		}
		// 6.3.9.1 (2015/11/27) Exceptionをまとめます｡
		catch( final IOException | InterruptedException | RuntimeException ex ) {
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		}
	}

	/**
	 * 現在の状態を文字列で返します｡
	 *
	 * @return  現在の状態
	 * @og.rtnNotNull
	 */
	public static String information() {
		return pp.toString();
	}

	/**
	 * ProcessPool は､AbstractObjectPool を継承した ｵﾌﾞｼﾞｪｸﾄﾌﾟｰﾙです｡
	 *
	 * OpenOfficeのﾌﾟﾛｾｽをﾌﾟｰﾙします｡
	 *
	 * @version  4.0
	 * @author   Hiroki Nakamura
	 * @since    JDK5.0,
	 */
	protected static final class ProcessPool extends AbstractObjectPool<SOfficeProcess> {
		// 環境ﾌｧｲﾙ作成の識別用
		private int count	;

		/**
		 * 初期処理を行います｡
		 */
		protected ProcessPool() {
			super();		// 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
			init( 0, HybsSystem.sysInt( "REPORT_MAX_PROCESS_COUNT")
					, true, HybsSystem.sysInt( "REPORT_PROCESS_ALIVE" ) );
		}

		/**
		 * soffieのﾌﾟﾛｾｽｵﾌﾞｼﾞｪｸﾄを作成します｡
		 *
		 * @og.rev 4.3.5.0 (2009/02/01) Exception ではなく､RuntimeException に変更
		 * @og.rev 5.1.7.0 (2010/06/01) TCP接続対応
		 *
		 * @return OpenOfficeのﾌﾟﾛｾｽ
		 */
		@Override
		protected SOfficeProcess createInstance() {
			SOfficeProcess soffice = null;
			try {
				// 5.1.7.0 (2010/06/01) TCP接続対応
				if( "TCP".equalsIgnoreCase( HybsSystem.sys( "REPORT_OOO_CONN_TYPE" ) ) ) {
					soffice = new SOfficeProcessTcp( "env_" + count, HybsSystem.sysInt( "REPORT_OOO_MIN_PORT" ) );
				}
				else {
					soffice = new SOfficeProcess( "env_" + count );
				}
				soffice.bootstrap();

				count++;
			}
			catch( final RuntimeException ex ) {
				System.out.println( "[ERROR]FACTORY:Failed to Connect Soffice! " + ex.getMessage() );
			}
			return soffice;
		}

		/**
		 * ｵﾌﾞｼﾞｪｸﾄﾌﾟｰﾙから削除するときに呼ばれます｡
		 * このﾒｿｯﾄﾞで各ｵﾌﾞｼﾞｪｸﾄごとの終了処理を行います｡
		 *
		 * @param soffice OpenOfficeのﾌﾟﾛｾｽ
		 */
		@Override
		protected void objectFinal( final SOfficeProcess soffice ) {
			soffice.close();
		}
	}
}
