/*
 * 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.common;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.TreeMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;

import org.opengion.fukurou.db.ConnectionFactory;
import org.opengion.fukurou.util.Closer;
import org.opengion.fukurou.util.FindClassFiles;
import org.opengion.fukurou.util.LogWriter;

/**
 * ログインしたサーブレットコンテキストに対応したシステムパラメータデータを取得するクラスです。
 *
 * システムパラメータデータ(GE12)は、パラメータ(PARAM_ID)に対して、各種設定値(PARAM)を
 * 持っています。
 * 従来は、resource.システムパラメータ の一般設定用の システムパラメータ ファイルと
 * エンジン内部で持っている org/hsgw/hayabusa/resource/properties の
 * システムパラメータ ファイルにより維持されていましたが、システムパラメータ
 * 定義テーブル(GE12)と、commom/SystemData.java クラスによる管理に変更されました。
 * 
 * システムパラメータは、ＤＢへのアクセスを決定するため、初期設定値を定義する必要があります。
 * これは、、アプリケーション・ディスクリプタ(WEB-INF/web.xml)に、context-param として、
 * キーと値のセットで、初期アクセス用の情報を渡します。
 * システムパラメータ定義テーブル(GE12)には、SYSTEM_IDとして、通常のシステムIDと、
 * エンジンパラメータがあります。エンジンパラメータは、SYSTEM_ID='**'として、登録
 * されています。
 *
 * <table border="1" frame="box" rules="all" >
 *   <tr><th>種類        </th><th>SYSTEM_ID</th><th>KBSAKU</th><th>説明</th></tr>
 *   <tr><td>エンジン共通</td><td>**       </td><td>0     </td><td>エンジン共通で定義しているパラメータ</td></tr>
 *   <tr><td>エンジン個別</td><td>個別     </td><td>0     </td><td>システム毎にエンジンが登録しているパラメータ</td></tr>
 *   <tr><td>システム共通</td><td>**       </td><td>1     </td><td>システム毎にエンジンが登録しているパラメータ</td></tr>
 *   <tr><td>システム個別</td><td>個別     </td><td>1     </td><td>システム毎に各自が独自に登録しているパラメータ</td></tr>
 * </table>
 *
 * アプリケーション・ディスクリプタ(WEB-INF/web.xml)設定情報
 * <table border="1" frame="box" rules="all" >
 *   <tr><th>パラメータ	</th><th>設定値例</th><th>解説</th></tr>
 *   <tr><td>SYSTEM_ID	</td><td>GE						</td>このアプリケーションのシステムID</tr>
 *   <tr><td>TOMCAT_PORT</td><td>8823					</td>Tomcat起動時ポート番号</tr>
 *   <tr><td>LOG_FILE	</td><td>log/log_$(yyyyMMdd).txt</td>JSPアクセスログ出力先</tr>
 * </table>
 *
 * ※ 5.6.7.0 (2013/07/27) 
 *    InitFileLoader により、特定のクラスパス内の 拡張XDK 形式の xml ファイル を処理します。
 *    クラスパスが、"/resource" 以下のすべての xml ファイルは、DBIDが RESOURCE の接続先に対して処理されます。
 *    クラスパスが、"/xml" 以下のすべての xml ファイルは、DBIDが DEFAULT の接続先に対して処理されます。
 *    各クラスパス以下のファイルは、実フォルダでも、jar形式に圧縮された形式でも処理されます。
 *
 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
 * @og.rev 4.0.0.0 (2007/10/26) loadDBResourceのコネクションをFactoryから取るように変更
 * @og.group 初期化
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class SystemParameter {

	/**
	 *	デフォルトコンストラクターをprivateにして、
	 *	オブジェクトの生成をさせないようにする。
	 *
	 */
	private SystemParameter() {}

	/** plugin クラスの取得用クラスパス指定	{@value}	*/
	public static final String PLUGIN = "org/opengion/plugin";

	/** システム共通/個別パラメータ(SYSTEM_ID in ('**',?) KBSAKU != '0')の一括読込のクエリー	{@value}	*/
//	public static final String QUERY = "SELECT PARAM_ID,PARAM,FGCRYPT"
//									+ " FROM GE12 WHERE SYSTEM_ID IN (?,'**')"
//									+ " AND ( CONTXT_PATH IS NULL OR CONTXT_PATH = ?)"
//									+ " AND FGJ='1' AND KBSAKU != '0'"
//									+ " ORDER BY SYSTEM_ID,CONTXT_PATH DESC,KBSAKU,SEQNO" ;
	// 4.0.0.0 (2007/10/05) CONTXT_PATH に、DEFAULT '**' NOT NULL 制約を付ける。
	/** システム共通/個別パラメータ(SYSTEM_ID in ('**',?) KBSAKU > '0')の一括読込のクエリー	{@value}	*/
	public static final String QUERY = "SELECT PARAM_ID,PARAM,FGCRYPT"
									+ " FROM GE12 WHERE SYSTEM_ID IN (?,'**')"
									+ " AND CONTXT_PATH IN (?,'**')"
									+ " AND FGJ='1' AND KBSAKU > '0'"
									+ " ORDER BY SYSTEM_ID,CONTXT_PATH,FGJ,KBSAKU,SEQNO" ;

	/** システム共通/個別パラメータ(SYSTEM_ID in ('**',?) KBSAKU > '0')の一括登録のクエリー	{@value}	*/
	public static final String INS_SYS = "INSERT INTO GE12"
									+ " ( SYSTEM_ID,CONTXT_PATH,PARAM_ID,SEQNO,PARAM,TITLE,CONTENTS,PARAM_LVL,FGJ,KBSAKU )"
									+ " SELECT"
									+ " ?,?,?,?,?,TITLE,CONTENTS,PARAM_LVL,'1','0'"
									+ " FROM GE12 WHERE SYSTEM_ID='**' AND FGJ='1' AND KBSAKU='0' AND PARAM_ID=?" ;

	/** エンジン個別(SYSTEM_ID='個別' KBSAKU='0'  CONTXT_PATH='自身')パラメータの一括削除のクエリー	{@value}	*/
	public static final String DEL_SYS = "DELETE FROM GE12 WHERE SYSTEM_ID=? AND KBSAKU='0' AND CONTXT_PATH=?";

	/** システムパラメータ構築中のエラーをセットしていきます。	*/
	private static final List<String> errMsgList = new ArrayList<String>();

	/**
	 * コンテキスト毎に システムパラメータオブジェクトを作成します。
	 * このクラスのスタートポイントメソッドになります。
	 *
	 * @og.rev 4.1.0.1 (2008/01/23) ログ出力先の変更(全てLogWriter経由で出力)
	 * @og.rev 5.5.4.4 (2012/07/20) LogWriter.log 追加
	 * @og.rev 5.5.4.4 (2012/07/20) SQLException は、catch しているので、loadDBResource からは、throws されない。
	 *
	 * @param context Servletコンテキスト
	 *
	 * @return	システムパラメータのマップ
	 */
	public static Map<String,String> makeSystemParameter( final ServletContext context ) {
		// 4.1.0.1 (2008/01/23) ログ出力は全てLogWriterで行うため、廃止
//		try {
//			String file = context.getInitParameter( "LOG_FILE" );
//			if( file != null ) {
//				// 日付フォームのファイル名を変換します。
//				DateSet dateSet = new DateSet();
//				file = dateSet.changeString( file );
//				// 4.1.0.0 (2008/01/12) 絶対パスでない場合は連結
//				if( ! new File( file ).isAbsolute() ) {
//					String path = context.getRealPath( "/" );
//					file = path + file;
//				}
//
//				// append モードで自動フラッシュON
//				System.setErr( new PrintStream( new FileOutputStream( file,true ),true ) );
//			}
//		}
//		catch( FileNotFoundException ex ) {
//			LogWriter.log( "web.xml InitParameter \"LOG_FILE\" is abusion." );
//			LogWriter.log( ex.getMessage() );
//		}

		errMsgList.clear() ;	// 呼び出し都度、エラーリストをクリアします。

		Map<String,String> engParam = null;
		Map<String,String> sysParam = null;
		try {
			String contextName = getContextName( context );

			System.out.println( "Context Initialized [" + contextName + "]  " + new Date() );
			System.out.print( "  Version [" + BuildNumber.VERSION_NO + " " );
			System.out.print( BuildNumber.BUILD_TYPE );
			System.out.println( "]" );

			// システムデータクラスより、エンジンパラメータ情報を初期設定します。
			// エンジンパラメータは、SystemData クラスの public static メンバーです。
			engParam = loadParameter( SystemData.class );

			// コンテキストより取得できるシステムパラメータ情報を取得します。
			// web.xml で設定したパラメータを取得します。
			// SYSTEM_ID,DB_DRIVER,DB_URL などは、loadDBResource で使用します。
			sysParam = loadInitialParameter( context,contextName );

			// システム個別に設定される、エンジン起動時情報を初期設定します。
			// エンジン起動時情報は、BuildNumber クラスの public static メンバーです。
			sysParam.putAll( loadParameter( BuildNumber.class ) );
			sysParam.putAll( loadParameter( PLUGIN ) );

			// GE12 データベースより読み取ります。
			// 引数のMapに読み込んだ値を追加した Map を返します。
			// つまり、システムパラメータ情報の上書きを行います。
	 		// 5.5.4.4 (2012/07/20) SQLException は、catch しているので、loadDBResource からは、throws されない。
	//		try {
				sysParam = loadDBResource( sysParam );
	//		}
	//		catch( SQLException ex ) {
	//			String errorMsg = "DB終了(close)処理を実行できませんでした。" + HybsSystem.CR
	//						+ ex.getMessage() + ":" + ex.getSQLState() ;
	//			LogWriter.log( ex );					// 5.5.4.4 (2012/07/20) LogWriter.log 追加
	//			errMsgList.add( errorMsg );
	//		}
		}
		catch( Throwable th ) {		// 3.6.1.0 (2005/01/05)
			LogWriter.log( th );						// 5.5.4.4 (2012/07/20) LogWriter.log 追加
			errMsgList.add( th.getMessage() );
		}
		finally {
			// 初期値のエンジンパラメータに個別のシステムパラメータを追加設定します。
			// つまり、エンジンパラメータ情報に上書きを行います。
			if( engParam != null ) {
				engParam.putAll( sysParam );
			}
			else {
				engParam = new HashMap<String,String>();
			}

			int errCnt = errMsgList.size();
			if( errCnt > 0 ) {
				StringBuilder buf = new StringBuilder();
				buf.append( "【システムパラメータの初期化に失敗しました。】" );
				buf.append( HybsSystem.CR );
				buf.append( "Tomcat の設定状況をご確認ください。" );
				buf.append( HybsSystem.CR );
				buf.append( "========================================" );
				buf.append( HybsSystem.CR );

				for( int i=0; i<errCnt; i++ ) {
					buf.append( errMsgList.get(i) );
					buf.append( HybsSystem.CR );
					buf.append( "----------------------------------------" );
					buf.append( HybsSystem.CR );
				}
				System.out.println( buf );
				engParam.put( HybsSystem.LOCAL_CONTX_ERR_KEY,buf.toString() );
			}
		}
		return engParam ;
	}

	/**
	 * コンテキストより、アプリケーション設定情報を取得します。
	 * 初期値は、アプリケーション・ディスクリプタ(WEB-INF/web.xml)に
	 * context-param として、キーと値のセットで、初期アクセス用の情報を渡します。
	 * データベースへの接続は、WEB-INF の DBConfig.xml で設定された情報を使用します。
	 *
	 * ここでは、各コンテキスト毎の内部情報を取得します。その中には、
	 * BuildNumber クラスで定義されている各種フィールド属性も含まれます。
	 *
	 * REAL_PATH       : アドレス(/)に対する、実ディレクトリパス
	 * CONTEXT_NAME    : アクセス先の仮想フォルダ名(URLのコンテキスト名)
	 * JSP             : アクセス先のJSPフォルダ名(/URLのコンテキスト名/jsp)
	 * SYSTEM_ID       : web.xml で指定する、SYSTEM_ID       の設定値
	 * TOMCAT_PORT     : web.xml で指定する、Tomcat起動時ポート番号(8823)
	 * LOG_FILE        : web.xml で指定する、JSPアクセスログ出力先(log/log_$(yyyyMMdd).txt)
	 * SERVER_INFO     : サーバー情報     [例： HN50G5 ( 200.1.50.165 ) ]
	 * SERVLET_INFO    : サーブレット情報 [例： Apache Tomcat/5.5.9     ]
	 * TOMCAT_WORK     : Tomcatワークの位置 [例： H:\java\tomcat5.5.17\work\Catalina\localhost\ver4  ]
	 * TOMCAT_HOME     : Tomcat環境の位置   [例： H:\java\tomcat5.5.17  ]
	 * JAVA_HOME       : Java実行環境の位置 [例： H:\java\jdk150\jre ]
	 * ENGINE_INFO     : バージョン番号 [例： 4.3.6.6 ]
	 *
	 * RESOURCE_DBID   : "RESOURCE" 固定値を設定。WEB-INF/web.xml で指定しても無効です。
	 *
	 * @og.rev 4.1.0.0 (2007/12/27) web.xmlからTOMCAT_PORTを読む処理を追加
	 * @og.rev 4.2.0.0 (2008/02/18) TOMCAT_PORTを環境変数から取得するよう変更
	 * @og.rev 4.2.0.0 (2008/02/20) web.xmlでSYSTEM_IDが空白の場合に大文字コンテキスト名が設定されるよう変更
	 * @og.rev 4.3.6.6 (2009/05/15) コンテキスト単位にエンジンバージョン情報を持つ(バージョンアップ判定用)
	 * @og.rev 5.6.7.1 (2013/08/09) RESOURCE_DBID の値に、"RESOURCE" を設定しておきます。
	 * @og.rev 5.6.7.3 (2013/08/23) TOMCAT_HOME を追加
	 *
	 * @param context Servletコンテキスト
	 * @param contextName コンテキスト名
	 *
	 * @return	システムパラメータのマップ
	 */
	private static Map<String,String> loadInitialParameter( final ServletContext context,final String contextName ) {
		Map<String,String> map = new LinkedHashMap<String,String>();

		// コンテキストの初期化パラメータ他の情報を登録しておきます。
		Enumeration<?> enume = context.getInitParameterNames() ;		// 4.3.3.6 (2008/11/15) Generics警告対応
		while( enume.hasMoreElements() ) {
			String key = (String)enume.nextElement();
			String val = context.getInitParameter( key );
			if( val != null && val.length() == 0 ) { val = null; }
			map.put( key,val );
		}

		// SYSTEM_IDがnullの場合は大文字のコンテキスト名を設定
		if( map.get( "SYSTEM_ID" ) == null ){ // 4.2.0.0 (2008/02/20)
			map.put( "SYSTEM_ID", contextName.toUpperCase( Locale.JAPAN ) );
		}

		// 各種システム情報を登録しておきます。
		map.put( "REAL_PATH"	,context.getRealPath( "/" ) );
		map.put( "CONTEXT_NAME"	,contextName );
		map.put( "JSP"			,"/" + contextName + "/jsp" );
		map.put( "SERVLET_INFO"	,context.getServerInfo() );
		map.put( "TOMCAT_WORK"	,String.valueOf( context.getAttribute( "javax.servlet.context.tempdir" ) ) );
		map.put( "TOMCAT_HOME"	,System.getProperty( "catalina.home" ) );	// 5.6.7.3 (2013/08/23)
		map.put( "JAVA_HOME"	,System.getProperty( "java.home" ) );

		map.put( "HOST_NAME"	,HybsSystem.HOST_NAME  );
		map.put( "HOST_ADRS"	,HybsSystem.HOST_ADRS  );
		map.put( "SERVER_INFO"	,HybsSystem.HOST_NAME  + " ( " + HybsSystem.HOST_ADRS + " )" );
		map.put( "ENGINE_INFO"	,BuildNumber.ENGINE_INFO );
		String TOMCAT_PORT = System.getenv( "CONNECTOR_PORT" ); // 4.2.0.0 (2008/02/18) ポート番号を環境変数から取得に変更
		if( TOMCAT_PORT == null || TOMCAT_PORT.length() == 0 ) { // 互換性のためweb.xmlからの取得を残す
			TOMCAT_PORT = map.get( "TOMCAT_PORT" ); // 4.1.0.0 (2007/12/27)
		}

		String HOST_URL;
		if( TOMCAT_PORT == null || TOMCAT_PORT.length() == 0 ) { // 両者とも値が取得できない場合は**
			HOST_URL = "**";
		}
		else {
			HOST_URL = HybsSystem.HOST_NAME + ":" + TOMCAT_PORT + "/" + contextName + "/";
		}
		map.put( "HOST_URL", HOST_URL );

		// 5.6.7.1 (2013/08/09) RESOURCE_DBID の値に、"RESOURCE" を設定しておきます。
		map.put( "RESOURCE_DBID", "RESOURCE" );

		System.out.println( "    Load Initial Parameter [" + map.size() + "] finished." );
		return map ;
	}

	/**
	 * アプリケーション個別に設定しているリソースＤＢ(GE12)を取得します。
	 *
	 * データベースへの接続は、WEB-INF の DBConfig.xml で設定された情報を元に、
	 * org.opengion.fukurou.db.ConnectionFactory で接続先を取得します。
	 * ここでは、web.xml で定義された各アプリケーション個別のパラメ―タを取得します。
	 * SYSTEM_ID(必須) です。
	 *
	 * @og.rev 4.0.0.0 (2007/10/10) 接続先情報の管理見直し(コンテキスト初期設定)
	 * @og.rev 4.0.0.0 (2007/10/26) コネクションをファクトリーから取ってくるように変更
	 * @og.rev 4.3.6.5 (2009/05/08) dataパス内のXMLファイルも読み取るようにする
	 * @og.rev 4.3.6.6 (2009/05/15) ↑を廃止。自動インストール対応。
	 * @og.rev 5.1.2.0 (2010/01/01) connection.setAutoCommit は、ConnectionFactory で設定済みなので、コメントアウト
	 * @og.rev 5.1.9.0 (2010/08/01) 自動インストールの設定見直し(画面からのインストール対応)
	 * @og.rev 5.5.4.4 (2012/07/20) SQLException は、catch しているので、このメソッドからは、throws されない。
	 * @og.rev 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。
	 * @og.rev 5.6.6.1 (2013/07/12) xml パス内のXMLファイルがあれば、DB登録します。
	 * @og.rev 5.6.7.0 (2013/07/27) InitFileLoader で、resource以下は、DBID=RESOURCE xml以下は、DBID=DEFAULT に登録します。
	 * @og.rev 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
	 *
	 * @param	sysParam	入力システムパラメータマップ
	 *
	 * @return	システムパラメータのマップ
	 */
//	private static Map<String,String> loadDBResource( final Map<String,String> sysParam ) throws SQLException {
	private static Map<String,String> loadDBResource( final Map<String,String> sysParam ) {
		String SYSTEM_ID		= sysParam.get( "SYSTEM_ID" );
		String CONTEXT_NAME		= sysParam.get( "CONTEXT_NAME" );	// コンテキスト別システムリソース
		String HOST_URL			= sysParam.get( "HOST_URL" );		// 4.1.0.0 (2007/12/21) TOMCATへのアクセス用
		String RESOURCE_DBID	= sysParam.get( "RESOURCE_DBID" );	// 5.5.4.5 (2012/07/27) 初期起動時のDB接続先

		// 必須項目チェックを行います。SYSTEM_IDは必須です。
		// これは、web.xml で定義が必要です。
		// 4.0.0.0 (2007/10/23)接続情報XML化につきDB_URLチェックを削除
		if( ( SYSTEM_ID == null || SYSTEM_ID.length() == 0 ) ) {
			String errorMsg = "システムパラメータの必須項目(SYSTEM_ID,DB_URL)が null です。" + HybsSystem.CR
							+ "SYSTEM_ID=[" + SYSTEM_ID + "] " + HybsSystem.CR
							+ "Versino=[" + BuildNumber.VERSION_NO + "] " + HybsSystem.CR ;
			errMsgList.add( errorMsg );
			return sysParam ;
		}

//		Connection 			connection 	= null;
		Connection 			defConn 	= null;			// 5.6.7.0 (2013/07/27) DBID=DEFAULT  のコネクション
		Connection 			rscConn 	= null;			// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
		PreparedStatement 	pstmt		= null;
		ResultSet 			resultSet 	= null;
		boolean 			errFlag 	= true;
		try {
			// 4.0.0.0(2007/10/25)ConnectionをConnectionFactory経由で取得するように変更する。
			// コンテキスト名で接続しにいく。ApplicationInfoは使わないのでnull
			ConnectionFactory.init( CONTEXT_NAME, null ); // ConnectionFactoryの初期化
//			connection = ConnectionFactory.connection( null, null );
//			connection = ConnectionFactory.connection( RESOURCE_DBID, null );	// 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。
//			connection.setAutoCommit( false );		// 5.1.2.0 (2010/01/01)

			defConn = ConnectionFactory.connection( null, null );			// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
			rscConn = ConnectionFactory.connection( RESOURCE_DBID, null );	// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション

			// 4.3.6.6 (2009/05/15)
			// 5.1.9.0 (2010/08/01) 自動インストールの設定見直し(画面からのインストール対応)
//			SystemInstaller installer = new SystemInstaller( connection, new PrintWriter( System.out, true ) );
			SystemInstaller installer = new SystemInstaller( defConn , rscConn , new PrintWriter( System.out, true ) );
			installer.autoInsUpd( SYSTEM_ID, CONTEXT_NAME, HOST_URL );

			// resource パス内のXMLファイルがあれば、先にDB登録します。(DBID=RESOURCE)
//			InitFileLoader loader = new InitFileLoader( connection );
			InitFileLoader loader = new InitFileLoader( rscConn );		// このコンストラクタは/resource フォルダを検索します。
			loader.loadInitFiles( true ) ;

			// 5.6.6.1 (2013/07/12) xml パス内のXMLファイルがあれば、DB登録します。
//			loader = new InitFileLoader( connection, "xml" );
			loader = new InitFileLoader( defConn, "xml" );
			loader.loadInitFiles( true ) ;

			// コンテキスト単位のシステムパラメータを GE12 に設定します。
			// dbXMLResourceInsert の後に登録する必要があります。
//			dbResourceUpdate( connection,sysParam );
//			connection.commit();
			dbResourceUpdate( rscConn,sysParam );			// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
			rscConn.commit();

			// DBを検索して、ユーザー設定リソース情報を取得します。
//			pstmt = connection.prepareStatement( QUERY );
			pstmt = rscConn.prepareStatement( QUERY );		// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
			pstmt.setString( 1,SYSTEM_ID );
			pstmt.setString( 2,HOST_URL ); // 4.1.0.0 (2007/12/21)
			Map<String,String> userMap = new HashMap<String,String>(100);
			resultSet = pstmt.executeQuery();

			while( resultSet.next() ) {
				String key   = resultSet.getString(1);
				String val   = resultSet.getString(2);
				if( val != null && val.length() == 0 ) { val = null; }
				userMap.put( key,val );
			}

			System.out.println( "    Load DB Resource [" + userMap.size() + "] finished." );
			// リソースをマージします。
			sysParam.putAll( userMap );
			errFlag = false;	// エラーでない
		}
		catch (SQLException ex) {
//			Closer.rollback( connection );
			Closer.rollback( defConn );			// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
			Closer.rollback( rscConn );			// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
			LogWriter.log( ex );
			String errorMsg = ex.getMessage() + ":" + ex.getSQLState() ;
			errMsgList.add( errorMsg );
		}
		catch (UnsupportedEncodingException ex) {
//			Closer.rollback( connection );
			Closer.rollback( defConn );			// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
			Closer.rollback( rscConn );			// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
			LogWriter.log( ex );
			String errorMsg = "UTF-8 がサポートされていない Java VM は、正規VMではありません。"
							+ ex.getMessage();
			errMsgList.add( errorMsg );
		}
		catch (RuntimeException ex) {
//			Closer.rollback( connection );
			Closer.rollback( defConn );			// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
			Closer.rollback( rscConn );			// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
			LogWriter.log( ex );
			String errorMsg = "処理を実行できませんでした。" + HybsSystem.CR
						+ " SYSTEM_ID = [" + SYSTEM_ID + "]" + HybsSystem.CR
						+ ex.getMessage() ;
			errMsgList.add( errorMsg );
		}
		finally {
			Closer.resultClose( resultSet );
			Closer.stmtClose( pstmt );
			if( errFlag ) {
//				ConnectionFactory.remove( connection, null );
				ConnectionFactory.remove( defConn, null );				// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
//				ConnectionFactory.remove( rscConn, null );				// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
				ConnectionFactory.remove( rscConn, RESOURCE_DBID );		// 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
			}
			else {
//				ConnectionFactory.close( connection, null );
				ConnectionFactory.close( defConn, null );				// 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
//				ConnectionFactory.close( rscConn, null );				// 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
				ConnectionFactory.close( rscConn, RESOURCE_DBID );		// 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
			}
		}

		return sysParam ;
	}

	/**
	 * エンジン内部定義の初期リソース情報をDB(GE12)に登録します。
	 *
	 * 初期リソース情報は、KBSAKU='0' で登録されている情報で、一旦すべて削除
	 * してから、全てのリソース情報を追加するという形をとります。
	 * ただし、属性情報(名称や概要など)を別途登録する場合は、全てを
	 * 削除せずに、UPDATE する方向で検討したいと思います。
	 * なお、この情報をDB登録する理由は、リソースの設定値を変えたい場合に、
	 * キーが判らない(JavaDOCからしか読み取れない)のでは不便な為に
	 * 用意しておくだけで、内部では SystemData オブジェクトとして定義
	 * されている値を使用するため、このデータベース値は、使用していません。
	 *
	 * @param	conn	登録用コネクション(リソース用)
	 * @param	map		入力システムパラメータマップ
	 * @throws	SQLException
	 */
	private static void dbResourceUpdate( final Connection conn,final Map<String,String> map )
				throws SQLException {

		String systemId = map.get( "SYSTEM_ID" );
		String HOST_URL = map.get( "HOST_URL" ); // 4.1.0.0 (2007/12/21)

		// 既存の設定値を全件DELETEします。
		int delCnt;
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement( DEL_SYS );
			pstmt.setString( 1, systemId );
			pstmt.setString( 2, HOST_URL ); // 4.1.0.0 (2007/12/21)
			delCnt = pstmt.executeUpdate();
		}
		finally {
			Closer.stmtClose( pstmt );
		}

		// 新設定値を全件INSERTします。
		Set<String> keyset = map.keySet();
		String[] keys = keyset.toArray( new String[keyset.size()] );

		int insCnt = 0;
		try {
			pstmt = conn.prepareStatement( INS_SYS );
			for( int i=0; i<keys.length; i++ ) {
				pstmt.setString( 1,systemId );
				pstmt.setString( 2,HOST_URL);
				pstmt.setString( 3,keys[i] );
				pstmt.setInt( 4,( i + 1 ) * 10 );
				pstmt.setString( 5,map.get( keys[i] ) );
				pstmt.setString( 6,keys[i] );
				insCnt += pstmt.executeUpdate();
			}
		}
		finally {
			Closer.stmtClose( pstmt );
		}

		System.out.print( "    DB Context Resource Reconfiguration " );
		System.out.println( "DELETE=[" + delCnt + "],INSERT=[" + insCnt + "] finished." );
	}

	/**
	 *  ServletContext の名称を取得します。
	 *
	 * コンテキストのアクセスされたパス( /training など )の名称を、
	 * 取得します。(アクセス先の仮想フォルダ名)
	 * 以前は、配備記述子(WEB-INF/web.xml)の display-name 要素を見て、
	 * 無ければ、実フォルダ名を返していました。
	 *
	 * @param  context Servletコンテキスト
	 *
	 * @return コンテキストのコンテキスト名
	 */
	private static String getContextName( final ServletContext context ) {
		String name = null;
		try {
			String path = context.getResource( "/" ).getFile();
			int idx = path.lastIndexOf( '/',path.length()-2 );
			name = path.substring( idx+1,path.length()-1 );
		}
		catch( MalformedURLException ex ) {
			String errMsg = "このパス名は、正しいフォームではありません。 "
						+ ex.getMessage();
			LogWriter.log( ex );
			errMsgList.add( errMsg );
		}
		return name ;
	}

	/**
	 * 指定のクラスの public static なフィールドキーと値のマップを作成します。
	 * 主に、エンジン関連のクラスにパラメータファイルをクラスとして定義し、
	 * エンジンとともに配布します。配布されたクラスを元に、パラメータを
	 * 読み取ります。
	 * この処理は リフレクションを使用してクラスの public static フィールドを
	 * 取得し、LinkedHashMap により、取得順をキープしたまま、Mapを返します。
	 *
	 * @param  cls クラスオブジェクト
	 *
	 * @return	システムパラメータのマップ
	 */
	private static Map<String,String> loadParameter( final Class<?> cls ) {	// 4.3.3.6 (2008/11/15) Generics警告対応
		Field[] field = cls.getFields();	// public フィールドのみ
		Map<String,String> map = new LinkedHashMap<String,String>( (int)(field.length * 1.5) ); // 負荷係数より大きい目に取る。

		try {
			for( int i=0; i<field.length; i++ ) {
				if( Modifier.isStatic( field[i].getModifiers() ) ) {
					map.put( field[i].getName() , (String)field[i].get( null ) );
				}
			}
		}
		catch( IllegalAccessException ex ) {
			LogWriter.log( ex );
			errMsgList.add( ex.getMessage() );
		}

		System.out.println( "    ClassLoad " + cls.getName() + " Parameter [" + map.size() + "] finished." );
		return map;
	}

	/**
	 * 指定のキーワードのファイルをクラスパスより取得し、キーと値のマップを作成します。
	 * 主に、エンジン関連のクラスにパラメータファイルをPlugInクラスとして定義し、配布します。
	 * この処理の取得に、クラスパスの順序が関係します。最初に取得されたキーは、あとから
	 * 読み取られたクラスパスは、再セットしません。
	 *
	 * @og.rev 5.3.6.0 (2011/06/01) 並び順を、キーの名称順とする。
	 *
	 * @param	keyword	クラスオブジェクトを検索する元
	 *
	 * @return	キーと値のマップ
	 */
	private static Map<String,String> loadParameter( final String keyword ) {
//		Map<String,String> map = new HashMap<String,String>();
		Map<String,String> map = new TreeMap<String,String>();		// 5.3.6.0 (2011/06/01) 並び順を、キーの名称順とする。
		try {
			ClassLoader loader = Thread.currentThread().getContextClassLoader();
			Enumeration<URL> enume = loader.getResources( keyword );		// 4.3.3.6 (2008/11/15) Generics警告対応
			while( enume != null && enume.hasMoreElements() ) {
				URL url = enume.nextElement();		// 4.3.3.6 (2008/11/15) Generics警告対応
				// jar:file:/実ディレクトリ または、file:/実ディレクトリ
				String dir = url.getFile();
	//			System.out.println( dir );

				FindClassFiles filenames = new FindClassFiles( dir,keyword );
				String[] names = filenames.getFilenames();
				for( int i=0; i<names.length; i++ ) {
					String val = names[i];
					String key = val.substring( val.lastIndexOf( '.' )+1 );
					if( key.indexOf( '_' ) >= 0 && !map.containsKey( key ) && key.indexOf( '$' ) < 0 ) {
						map.put( key , val );
					}
				}
				System.out.println( "    FileCheck " + dir + " [" + names.length + "] find." );
			}
		}
		catch( IOException ex ) {
			LogWriter.log( ex );
			errMsgList.add( ex.getMessage() );
		}
		catch( RuntimeException ex ) {
			LogWriter.log( ex );
			errMsgList.add( ex.getMessage() );
		}
		System.out.println( "    FileLoad " + keyword + " Parameter [" + map.size() + "] finished." );
		return map;
	}
}
