/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/
package jp.co.ntt.lms.installer.lms;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

import jp.co.ntt.lms.installer.core.InstallerActionValue;
import jp.co.ntt.lms.installer.core.InstallerException;
import jp.co.ntt.lms.installer.core.InstallerValues;
import jp.co.ntt.lms.installer.core.util.InstallerFileUtil;
import jp.co.ntt.lms.installer.core.util.InstallerProcessUtil;
import jp.co.ntt.lms.installer.core.util.InstallerUiUtil;
import jp.co.ntt.lms.installer.util.LoggerUtil;

/**
 * Oracleにデータベース・テーブルを作成するアクション・クラス。
 * @author T.Nishiki
 */
public class CreateDbOracleAction extends CreateDbAbstractAction {
	/**
	 * ログ出力オブジェクト。
	 */
	private static Logger _logger;

	/**
	 * スタティック・イニシャライザ。
	 */
	static {
		_logger = LoggerUtil.getLogger(CreateDbOracleAction.class);
	}

	/**
	 * 対応するデータベースの種別を戻します。<br>
	 * @return データベース種別を戻します。
	 */
	protected String getDbCategory() {
		return "1";
	}

	/**
	 * データベース・テーブルを作成する共通アクション。
	 * @param values インストーラ共通データ・オブジェクト。
	 * @return nullを戻します。
	 * @throws InstallerException データベースアクセス中に例外が発生した場合。
	 */
	public InstallerActionValue action(InstallerValues values)
		throws InstallerException {

		LoggerUtil.methodHead(_logger, "action(InstallerValues)");
		try {
			// 開始メッセージ出力
			InstallerUiUtil.outputMessageAsTitle(
				values,	"CreateDbAbstractAction.head");
			// =================================================================
			// データベース初期化用に入力した値を取得
			ConfirmDbInitializeValue initializeValue
				= (ConfirmDbInitializeValue)values.getActionValue(
					ConfirmDbInitializeAction.class.getName());
			// データベース管理者ユーザ
			String dbaUser = initializeValue.getDbaUser();
			// データベース管理者パスワード
			String dbaPass = initializeValue.getDbaPassword();
			// ネットサービス名
			Properties extendProperties
				= initializeValue.getExtendProperties();
			String serviceName = extendProperties.getProperty(
						"db.extend.oracle.netservice.name");
			// =================================================================
			// Managerの設定値を使用
			ManagerInputValue inputValue = (ManagerInputValue)
				values.getActionValue(ManagerInputAction.class.getName());
			// 接続ユーザ名
			String user = inputValue.getDbUser();
			// 接続ユーザ・パスワード
			String pass = inputValue.getDbPassword();
			////////////////////////////////////////////////////////////////////
			// SQL実行
			// =================================================================
			// テーブルスペース作成
			executeSqlPlusQueryType(values, "create.database",
				dbaUser, dbaPass, serviceName);
			// ユーザ作成
			executeSqlPlusQueryType(values, "create.user",
				dbaUser, dbaPass, serviceName);
			// 通常のクエリ（前半）
			executeSqlPlusQueryType(values, "common.first",
				user, pass, serviceName);
			// オプションLO用クエリ
			String[] optionLOKeys = values.getOptionLOKeys();
			for (int i = 0; i < optionLOKeys.length; i++) {
				if ( values.getOptionLOSelected(optionLOKeys[i]) == false ) {
					continue;
				}
				executeSqlPlusQueryType(values,
					"optionLO." + optionLOKeys[i],
					user, pass, serviceName);
			}
			// 通常のクエリ（後半）
			executeSqlPlusQueryType(values, "common.second",
				user, pass, serviceName);
			// 終了メッセージ出力
			InstallerUiUtil.outputMessage(
				values, "CreateDbAbstractAction.tail", true);
		}
		catch (InstallerException e) {
			// エラーメッセージ出力
			InstallerUiUtil.outputMessage(
				values, "CreateDbAbstractAction.error", true);
			throw e;
		}
		catch (Exception e) {
			// エラーメッセージ出力
			InstallerUiUtil.outputMessage(
				values, "CreateDbAbstractAction.error", true);
			LoggerUtil.throwable(_logger, e);
			throw new InstallerException(e);
		}
		LoggerUtil.methodTail(_logger, "action(InstallerValues)");
		return null;
	}


	/**
	 * 指定されたクエリタイプのクエリーを実行します。<br>
	 *
	 * @param values インストーラ共通データ・オブジェクト。
	 * @param queryType 実行するクエリー種別。
	 * @param user 接続ユーザ名。
	 * @param password 接続ユーザ・パスワード。
	 * @param serviceName 接続する際に使用するサービス名。
	 * @throws InstallerException データベースアクセス中に例外が発生した場合。
	 */
	private void executeSqlPlusQueryType(
			InstallerValues values, String queryType,
			String user, String password, String serviceName)
			throws InstallerException {
		// メソッド開始ログ
		LoggerUtil.methodHead(_logger,
			"executeSqlPlusQueryType(InstallerValues," + queryType + ","
				+ user + "," + password + "," +  serviceName + ")");
		// リソースからSQLファイルを生成
		File[] queryFiles = makeSqlFilesFromResource(values, queryType);
		// 生成したSQLファイルをSQL Plus通じて実行
		for (int i = 0; i < queryFiles.length; i++) {
			executeSqlPlus(values, user, password, serviceName, queryFiles[i]);
		}
		// メソッド終了ログ
		LoggerUtil.methodTail(_logger,
			"executeSqlPlusQueryType(InstallerValues," + queryType + ","
				+ user + "," + password + "," +  serviceName + ")");
	}

	/**
	 * SQL Plusのコマンドを通じて、指定したSQLファイルを実行します。
	 * @param values インストーラ共通データ・オブジェクト。
	 * @param user 接続ユーザ名。
	 * @param password 接続ユーザ・パスワード。
	 * @param serviceName 接続する際に使用するサービス名。
	 * @param file SQLスクリプトファイル。
	 * @throws InstallerException SQL Plusのコマンド呼び出し失敗などした場合。
	 */
	private void executeSqlPlus(
		InstallerValues values,
		String user, String password, String serviceName, File file)
		throws InstallerException {
		// メソッド開始ログ
		LoggerUtil.methodHead(_logger,
			"executeSqlPlus(InstallerValues," + user + ","
				+ password + "," +  serviceName + "," + file + ")");
		try {
 			// コネクション文字列を生成。
			String connectionString
				= user + "/" + password + "@" + serviceName;
			// SQL Plusコマンド生成
			String[] commandArray = new String[] {
				"sqlplus", "-S", "-L", connectionString,
				"@" + file.getAbsolutePath(),
			};
			// SQL Plusコマンド実行
			InstallerProcessUtil.excuteCommands(commandArray);
		}
		catch (InstallerException e) {
			throw e;
		}
		catch (Exception e) {
			LoggerUtil.throwable(_logger, e);
			throw new InstallerException(e);
		}
		// メソッド終了ログ
		LoggerUtil.methodTail(_logger,
			"executeSqlPlus(InstallerValues," + user + ","
				+ password + "," +  serviceName + "," + file + ")");
	}

	/**
	 * 指定されたクエリタイプのクエリーをファイルとして生成します。<br>
	 *
	 * @param values インストーラ共通データ・オブジェクト。
	 * @param queryType クエリー種別。
	 * @return 生成したSQLファイルの配列。生成しなかった場合は要素数０の配列。
	 * @throws InstallerException データアクセス中に例外が発生した場合。
	 */
	private File[] makeSqlFilesFromResource(
		InstallerValues values, String queryType)
		throws InstallerException {
		// メソッド開始ログ
		LoggerUtil.methodHead(_logger,
			"makeSqlFilesFromResource(InstallerValues," + queryType + ")");

		List fileList = new ArrayList();
		try {
			String[][] queryResources
				= values.getQueryResources(queryType);
			// クエリ分がない場合は終了
			if (queryResources.length > 0) {
				for (int i = 0; i < queryResources.length; i++) {
					// クエリファイル生成
					List sqlFileList = makeSqlFileFromResource(values,
						queryResources[i][0], queryResources[i][1]);
					fileList.addAll(sqlFileList);
				}
			}
		}
		catch (InstallerException e) {
			throw e;
		}
		catch (Exception e) {
			LoggerUtil.throwable(_logger, e);
			throw new InstallerException(e);
		}
		// 配列に変換
		File[] sqlFiles = new File[fileList.size()];
		fileList.toArray(sqlFiles);
		// メソッド終了ログ
		LoggerUtil.methodTail(_logger,
			"makeSqlFilesFromResource(InstallerValues," + queryType + ")",
			sqlFiles);
		return sqlFiles;
	}

	/**
	 * 与えられたリソースパスからSQLファイルをテンポラリに作成します。<br>
	 * SQLファイルは、リソースから読込、&quot;;&quot;ごとに
	 * 出力するファイルを区切ります。
	 * @param values インストーラ共通データオブジェクト。
	 * @param resourcePath 取得するファイル・リソースパス。
	 * @param encode 入力ストリームを読み込むエンコード。
	 * @return 生成したファイルオブジェクトを要素とするリスト。
	 * @throws InstallerException　リソース取得に失敗した場合、
	 *          ファイル生成に失敗した場合にスローします。
	 */
	private List makeSqlFileFromResource(
		InstallerValues values, String resourcePath, String encode)
		throws InstallerException {

		// メソッド開始ログ
		LoggerUtil.methodHead(_logger,
			"makeSqlFileFromResource(InstallerValues,"
				+ resourcePath + ","  + encode + ")");
		// このメソッドの戻り値
		List sqlFileList = new ArrayList();
		boolean successFlag = false;
		BufferedReader reader = null;
		BufferedWriter writer = null;
		try {
			// リソースからの文字入力ストリームを生成
			reader = new BufferedReader(
				new InputStreamReader(
					values.getResourcePathStream(resourcePath), encode));
			////////////////////////////////////////////////////////////////////
			// 出力先ファイルの設定
			File tempDir = values.getInstallerTempDir();
			File sqlBaseFile = new File(tempDir, resourcePath);
			String sqlBaseFileName = sqlBaseFile.getName();
			// 出力先ディレクトリに生成
			File madeFileParentDir = sqlBaseFile.getParentFile();
			if (madeFileParentDir.exists() == false) {
				madeFileParentDir.mkdirs();
			}
			// =================================================================
			// ファイル名の接頭部分と拡張子を決定
			String fileNamePrefix = null;
			String fileNameSuffix = null;
			int periodPos = sqlBaseFileName.lastIndexOf(".");
			if (periodPos <= 0) {
				fileNamePrefix = "temp";
				fileNameSuffix = ".sql";
			}
			else {
				fileNamePrefix = sqlBaseFileName.substring(0, periodPos);
				fileNameSuffix = ".sql";
			}
			// =================================================================
			// 最初のファイルを生成
			File sqlFile = File.createTempFile(
					fileNamePrefix, fileNameSuffix, madeFileParentDir);
			sqlFileList.add(sqlFile);
			// ファイルへの文字出力ストリームを生成
			writer = new BufferedWriter( new FileWriter(sqlFile));
			// =================================================================
			// データの移し変え
			while (true) {
				// 入力ストリームから読込
				String line = reader.readLine();
				if (line == null) {
					break;
				}
				// 読み込んだ文字列を置換
				line = replace(values, line);
				if (line.trim().toLowerCase().startsWith("exit;") == true) {
					continue;
				}
				// 出力ストリームに出力
				writer.write(line);
				writer.newLine();
				// =============================================================
				// SQL文の区切り文字を見つけた場合、
				// sqlplusを終了するコマンドを付加して、ファイルを一旦区切る。
				if (line.trim().endsWith(";")) {
					writer.write("EXIT;");
					writer.newLine();
					writer.close();
					// 新しいファイルを生成
					sqlFile = File.createTempFile(
						fileNamePrefix, fileNameSuffix, madeFileParentDir);
					sqlFileList.add(sqlFile);
					// 新しいファイルに対する文字出力ストリームを生成
					writer = new BufferedWriter( new FileWriter(sqlFile));
				}
			}
			// 最後のファイルに衆力コマンドを付加。
			writer.write("EXIT;");
			writer.newLine();
			// tryブロック終端まで到達
			successFlag = true;
		}
		catch (InstallerException e) {
			throw e;
		}
		catch (Exception e) {
		}
		finally {
			InstallerException thrown = null;
			// 入力ストリームのクローズ
			try {
				InstallerFileUtil.close(reader, successFlag);
			}
			catch (InstallerException e) {
				thrown = e;
			}
			// 出力ストリームのクローズ
			try {
				InstallerFileUtil.close(writer, successFlag);
			}
			catch (InstallerException e) {
				if ( thrown == null) {
					thrown = e;
				}
			}
			if (thrown != null) {
				throw thrown;
			}
		}
		// メソッド終了ログ
		LoggerUtil.methodTail(_logger,
			"makeSqlFileFromResource(InstallerValues,"
				+ resourcePath + ","  + encode + ")",
			sqlFileList);
		return sqlFileList;
	}

	/**
	 * 与えられたクエリー文の置換を行います。
	 * @param values インストーラ共通データオブジェクト。
	 * @param queryLine 変換対象のクエリー文。
	 * @return 置換後のクエリー文。
	 */
	protected String replace(InstallerValues values, String queryLine) {

		// =====================================================================
		// マネージャ設定値
		ManagerInputValue managerValue
			= (ManagerInputValue)values.getActionValue(
				ManagerInputAction.class.getName());
		queryLine = queryLine.replaceAll(
				"\"%XMS2_USER_NAME%\"", managerValue.getDbUser());
		queryLine = queryLine.replaceAll(
				"%XMS2_USER_NAME%", managerValue.getDbUser());
		queryLine = queryLine.replaceAll(
				"\"%XMS2_USER_PASSWORD%\"", managerValue.getDbPassword());
		queryLine = queryLine.replaceAll(
				"%XMS2_USER_PASSWORD%", managerValue.getDbPassword());
		queryLine = queryLine.replaceAll(
				"%DB_ENCODE%", managerValue.getDbEncode());
		// =====================================================================
		// 独自入力値
		ConfirmDbInitializeValue initializeValue = (ConfirmDbInitializeValue)
			values.getActionValue(ConfirmDbInitializeAction.class.getName());
		Properties extendProperties = initializeValue.getExtendProperties();
		// 表領域名称
		queryLine = queryLine.replaceAll(
				"\"%TABLESPACE%\"",
				 extendProperties.getProperty(
					"db.extend.oracle.tablespace.name"));
		queryLine = queryLine.replaceAll(
				"%TABLESPACE%",
				 extendProperties.getProperty(
					"db.extend.oracle.tablespace.name"));
		// =====================================================================
		// メール管理者用メールアドレス
		MailInputValue mailValue
			= (MailInputValue)values.getActionValue(
					MailInputAction.class.getName());
		if (mailValue != null) {
			String siteAdministratorMail
				= mailValue.getSiteAdministratorMail();
			if (siteAdministratorMail != null) {
				siteAdministratorMail = "null";
			}
			else {
				siteAdministratorMail = "'" + siteAdministratorMail + "'";
			}
			queryLine = queryLine.replaceAll(
					"%MAILSITEADMIN%", siteAdministratorMail);
		}
		else {
			queryLine = queryLine.replaceAll(
					"%MAILSITEADMIN%", "null");
		}
		// 変換後の文字列を戻す。
		return queryLine;
	}
}
