001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.common;
017
018import java.io.PrintWriter;
019import java.io.IOException;
020import java.io.UnsupportedEncodingException;
021import java.lang.reflect.Field;
022import java.lang.reflect.Modifier;
023import java.net.MalformedURLException;
024import java.net.URL;
025import java.sql.Connection;
026import java.sql.PreparedStatement;
027import java.sql.ResultSet;
028import java.sql.SQLException;
029import java.util.ArrayList;
030import java.util.Date;
031import java.util.Enumeration;
032import java.util.HashMap;
033import java.util.LinkedHashMap;
034import java.util.TreeMap;
035import java.util.List;
036import java.util.Locale;
037import java.util.Map;
038import java.util.Set;
039import java.util.Collections;                                                                                   // 6.4.3.2 (2016/02/19)
040
041import javax.servlet.ServletContext;
042
043import static org.opengion.fukurou.system.HybsConst.CR ;                                // 6.1.0.0 (2014/12/26)
044import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;              // 6.1.0.0 (2014/12/26) refactoring
045import org.opengion.fukurou.db.ConnectionFactory;
046import org.opengion.fukurou.system.BuildNumber;                                                 // 6.4.2.0 (2016/01/29) hayabusa.common.BuildNumber → fukurou.system.BuildNumber に移動
047import org.opengion.fukurou.system.Closer;
048import org.opengion.fukurou.util.FindClassFiles;
049import org.opengion.fukurou.system.LogWriter;
050import org.opengion.fukurou.system.ThrowUtil ;                                                  // 6.4.2.0 (2016/01/29)
051import org.opengion.fukurou.util.StringUtil;
052import org.opengion.fukurou.xml.XMLFileLoader;                                                  // 6.0.0.0 (2014/04/11)
053
054/**
055 * ログインしたサーブレットコンテキストに対応したシステムパラメータデータを取得するクラスです。
056 *
057 * システムパラメータデータ(GE12)は、パラメータ(PARAM_ID)に対して、各種設定値(PARAM)を
058 * 持っています。
059 * 従来は、resource.システムパラメータ の一般設定用の システムパラメータ ファイルと
060 * エンジン内部で持っている org/hsgw/hayabusa/resource/properties の
061 * システムパラメータ ファイルにより維持されていましたが、システムパラメータ
062 * 定義テーブル(GE12)と、commom/SystemData.java クラスによる管理に変更されました。
063 * 
064 * システムパラメータは、DBへのアクセスを決定するため、初期設定値を定義する必要があります。
065 * これは、、アプリケーション・ディスクリプタ(WEB-INF/web.xml)に、context-param として、
066 * キーと値のセットで、初期アクセス用の情報を渡します。
067 * システムパラメータ定義テーブル(GE12)には、SYSTEM_IDとして、通常のシステムIDと、
068 * エンジンパラメータがあります。エンジンパラメータは、SYSTEM_ID='**'として、登録
069 * されています。
070 *
071 * <table border="1" frame="box" rules="all" >
072 *   <caption>システムパラメータの説明</caption>
073 *   <tr><th>種類        </th><th>SYSTEM_ID</th><th>作成区分</th><th>説明</th></tr>
074 *   <tr><td>エンジン共通</td><td>**  </td><td>0:エンジン</td><td>エンジン共通で定義しているパラメータ                     </td></tr>
075 *   <tr><td>エンジン個別</td><td>個別  </td><td>0:エンジン</td><td>システム毎にエンジンが登録しているパラメータ </td></tr>
076 *   <tr><td>システム共通</td><td>**  </td><td>1:システム </td><td>システム毎にエンジンが登録しているパラメータ </td></tr>
077 *   <tr><td>システム個別</td><td>個別  </td><td>1:システム </td><td>システム毎に各自が独自に登録しているパラメータ        </td></tr>
078 * </table>
079 *
080 * <table border="1" frame="box" rules="all" >
081 *   <caption>アプリケーション・ディスクリプタ(WEB-INF/web.xml)設定情報</caption>
082 *   <tr><th>パラメータ      </th><th>設定値例                           </th><th>解説                                     </th></tr>
083 *   <tr><td>SYSTEM_ID  </td><td>GE                                             </td><td>このアプリケーションのシステムID     </td></tr>
084 *   <tr><td>TOMCAT_PORT</td><td>8823                                   </td><td>Tomcat起動時ポート番号        </td></tr>
085 *   <tr><td>LOG_FILE   </td><td>log/log_$(yyyyMMdd).txt</td><td>JSPアクセスログ出力先          </td></tr>
086 * </table>
087 *
088 * ※ 5.6.7.0 (2013/07/27)
089 *    InitFileLoader により、特定のクラスパス内の 拡張XDK 形式の xml ファイル を処理します。
090 *    クラスパスが、"/resource" 以下のすべての xml ファイルは、DBIDが RESOURCE の接続先に対して処理されます。
091 *    クラスパスが、"/xml" 以下のすべての xml ファイルは、DBIDが DEFAULT の接続先に対して処理されます。
092 *    各クラスパス以下のファイルは、実フォルダでも、jar形式に圧縮された形式でも処理されます。
093 *
094 * ※ 6.0.0.0 (2014/04/11)
095 *    InitFileLoader が廃止になり、代わりに、XMLFileLoader を使用します。処理自体は、ほぼ同様です。
096 *
097 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
098 * @og.rev 4.0.0.0 (2007/10/26) loadDBResourceのコネクションをFactoryから取るように変更
099 * @og.group 初期化
100 *
101 * @version  4.0
102 * @author   Kazuhiko Hasegawa
103 * @since    JDK5.0,
104 */
105public final class SystemParameter {
106
107        /** plugin クラスの取得用クラスパス指定       {@value}        */
108        public static final String PLUGIN = "org/opengion/plugin";
109
110        // 4.0.0.0 (2007/10/05) CONTXT_PATH に、DEFAULT '**' NOT NULL 制約を付ける。
111        /** システム共通/個別パラメータ(SYSTEM_ID in ('**',?) and KBSAKU &gt; '0')の一括読込のクエリー {@value}        */
112        public static final String QUERY = "SELECT PARAM_ID,PARAM,FGCRYPT"
113                                                                        + " FROM GE12 WHERE SYSTEM_ID IN (?,'**')"
114                                                                        + " AND CONTXT_PATH IN (?,'**')"
115                                                                        + " AND FGJ='1' AND KBSAKU > '0'"
116                                                                        + " ORDER BY SYSTEM_ID,CONTXT_PATH,FGJ,KBSAKU,SEQNO" ;
117
118        /** システム共通/個別パラメータ(SYSTEM_ID in ('**',?) and KBSAKU &gt; '0')の一括登録のクエリー {@value}        */
119        public static final String INS_SYS = "INSERT INTO GE12"
120                                                                        + " ( SYSTEM_ID,CONTXT_PATH,PARAM_ID,SEQNO,PARAM,TITLE,CONTENTS,PARAM_LVL,FGJ,KBSAKU )"
121                                                                        + " SELECT"
122                                                                        + " ?,?,?,?,?,TITLE,CONTENTS,PARAM_LVL,'1','0'"
123                                                                        + " FROM GE12 WHERE SYSTEM_ID='**' AND FGJ='1' AND KBSAKU='0' AND PARAM_ID=?" ;
124
125        /** エンジン個別(SYSTEM_ID='個別' KBSAKU='0'  CONTXT_PATH='自身')パラメータの一括削除のクエリー  {@value}        */
126        public static final String DEL_SYS = "DELETE FROM GE12 WHERE SYSTEM_ID=? AND KBSAKU='0' AND CONTXT_PATH=?";
127
128        /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ  {@value} */
129        private static final int DB_FETCH_SIZE = 1001 ;                                                 // HybsSystem に設定する前なので、HybsSystem.sysInt では取得できない。
130
131        /** システムパラメータ構築中のエラーをセットしていきます。 */
132        private static final List<String> ERR_MSG_LIST = new ArrayList<>();             // 6.4.1.1 (2016/01/16) errMsgList → ERR_MSG_LIST refactoring
133
134        /**
135         *      デフォルトコンストラクターをprivateにして、
136         *      オブジェクトの生成をさせないようにする。
137         *
138         */
139        private SystemParameter() {}
140
141        /**
142         * コンテキスト毎に システムパラメータオブジェクトを作成します。
143         * このクラスのスタートポイントメソッドになります。
144         *
145         * @og.rev 4.1.0.1 (2008/01/23) ログ出力先の変更(全てLogWriter経由で出力)
146         * @og.rev 5.5.4.4 (2012/07/20) LogWriter.log 追加
147         * @og.rev 5.5.4.4 (2012/07/20) SQLException は、catch しているので、loadDBResource からは、throws されない。
148         * @og.rev 5.7.2.0 (2014/01/10) Throwable の情報をもう少し詳細に出します。
149         * @og.rev 6.4.3.2 (2016/02/19) engParamとsysParamを初期設定して、同期化しておく。
150         * @og.rev 6.4.7.1 (2016/06/17) セキュリティの関連で、ログの詳細を出していませんでしたが、出すように変更します。
151         * @og.rev 6.8.6.0 (2018/01/19) ImageIO.write で、Java9で、NumberFormatException が出てしまう(暫定)対策。
152         *
153         * @param context Servletコンテキスト
154         *
155         * @return      システムパラメータのマップ
156         */
157        public static Map<String,String> makeSystemParameter( final ServletContext context ) {
158                ERR_MSG_LIST.clear() ;  // 呼び出し都度、エラーリストをクリアします。
159
160                // 6.4.3.2 (2016/02/19)
161                final Map<String,String> engParam = new LinkedHashMap<>();      // 最終的にマージして返す。
162                final Map<String,String> sysParam = new LinkedHashMap<>();      // DB書き戻し等あるので、設定中は分けておく。
163
164                try {
165                        // 6.8.6.0 (2018/01/19) ImageIO.write (暫定)対策
166                        // ※ ImageIO.getReaderMIMETypes(); での Java 9 has java.specification.version == 9 で、
167                        // Caused by: java.lang.NumberFormatException: For input string: "" が発生する対応
168                        // https://github.com/jai-imageio/jai-imageio-core/issues/24
169                        final String javaSpecVer = System.getProperty( "java.specification.version" );
170                        if( !javaSpecVer.startsWith( "1." ) ) {
171                                System.setProperty( "java.specification.version" , "1." + javaSpecVer );        // "java.specification.version" , "9" の場合、"1.9" に無理やり変更します。
172                        }
173
174                        final String contextName = getContextName( context );
175
176                        System.out.println( "Context Initialized [" + contextName + "]  " + new Date() );
177                        System.out.print( "  Version [" + BuildNumber.VERSION_NO + " " );
178                        System.out.print( BuildNumber.BUILD_TYPE );
179                        System.out.println( "]" );
180
181                        // システムデータクラスより、エンジンパラメータ情報を初期設定します。
182                        // エンジンパラメータは、SystemData クラスの public static メンバーです。
183                        loadParameter( engParam , SystemData.class );                           // 6.4.3.2 (2016/02/19)
184
185                        // コンテキストより取得できるシステムパラメータ情報を取得します。
186                        // web.xml で設定したパラメータを取得します。
187                        // SYSTEM_ID,DB_DRIVER,DB_URL などは、loadDBResource で使用します。
188                        loadInitialParameter( sysParam , context , contextName );       // 6.4.3.2 (2016/02/19)
189
190                        // システム個別に設定される、エンジン起動時情報を初期設定します。
191                        // エンジン起動時情報は、BuildNumber クラスの public static メンバーです。
192                        loadParameter( sysParam , BuildNumber.class );                          // 6.4.3.2 (2016/02/19)
193                        loadParameter( sysParam , PLUGIN );
194
195                        // GE12 データベースより読み取ります。
196                        // 引数のMapに読み込んだ値を追加した Map を返します。
197                        // つまり、システムパラメータ情報の上書きを行います。
198                        // 5.5.4.4 (2012/07/20) SQLException は、catch しているので、loadDBResource からは、throws されない。
199        //              try {
200                                loadDBResource( sysParam );                                                             // 6.4.3.2 (2016/02/19)
201        //              }
202        //              catch( final SQLException ex ) {
203        //              final String errMsg = "DB終了(close)処理を実行できませんでした。" + CR
204        //                                              + ex.getMessage() + ":" + ex.getSQLState() ;
205        //                      LogWriter.log( ex );                                    // 5.5.4.4 (2012/07/20) LogWriter.log 追加
206        //                      ERR_MSG_LIST.add( errMsg );
207        //              }
208                }
209                catch( final Throwable th ) {           // 3.6.1.0 (2005/01/05)
210                        LogWriter.log( th );                                            // 5.5.4.4 (2012/07/20) LogWriter.log 追加
211                        // 5.7.2.0 (2014/01/10) Throwable の情報をもう少し詳細に出します。
212                        final String errMsg = "処理を実行できませんでした。"  + CR ;
213        //                                      + th.getMessage()                                               + CR
214        //                                      + StringUtil.ogStackTrace( th ) ;
215        //              ERR_MSG_LIST.add( errMsg );
216        //              final String errMsg = "処理を実行できませんでした。"  + CR ;                  // 6.4.7.1 (2016/06/17)
217                        ERR_MSG_LIST.add( ThrowUtil.ogStackTrace( errMsg,th ) );                                // 6.4.2.0 (2016/01/29)
218                }
219                finally {
220                        // 初期値のエンジンパラメータに個別のシステムパラメータを追加設定します。
221                        // つまり、エンジンパラメータ情報に上書きを行います。
222                        // 6.3.9.1 (2015/11/27) engParam が null になることは無い(findbugs)。
223                                engParam.putAll( sysParam );
224
225                        final int errCnt = ERR_MSG_LIST.size();
226                        if( errCnt > 0 ) {
227                                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
228                                        .append( "【システムパラメータの初期化に失敗しました。】" )
229                                        .append( CR )
230                                        .append( "Tomcat の設定状況をご確認ください。" )
231                                        .append( CR )
232                                        .append( "========================================" )
233                                        .append( CR );
234
235                                for( int i=0; i<errCnt; i++ ) {
236                                        buf.append( ERR_MSG_LIST.get(i) )
237                                                .append( CR )
238                                                .append( "----------------------------------------" )
239                                                .append( CR );
240                                }
241                                System.out.println( buf );
242                                engParam.put( HybsSystem.LOCAL_CONTX_ERR_KEY,buf.toString() );
243                        }
244                }
245                return Collections.synchronizedMap( engParam ) ;                // 6.4.3.2 (2016/02/19) 同期化しておく。
246        }
247
248        /**
249         * コンテキストより、アプリケーション設定情報を取得します。
250         * 初期値は、アプリケーション・ディスクリプタ(WEB-INF/web.xml)に
251         * context-param として、キーと値のセットで、初期アクセス用の情報を渡します。
252         * データベースへの接続は、WEB-INF の DBConfig.xml で設定された情報を使用します。
253         *
254         * ここでは、各コンテキスト毎の内部情報を取得します。その中には、
255         * BuildNumber クラスで定義されている各種フィールド属性も含まれます。
256         *
257         * REAL_PATH       : アドレス(/)に対する、実ディレクトリパス
258         * CONTEXT_NAME    : アクセス先の仮想フォルダ名(URLのコンテキスト名)
259         * JSP             : アクセス先のJSPフォルダ名(/URLのコンテキスト名/jsp)
260         * SYSTEM_ID       : web.xml で指定する、SYSTEM_ID       の設定値
261         * TOMCAT_PORT     : web.xml で指定する、Tomcat起動時ポート番号(8823)
262         * LOG_FILE        : web.xml で指定する、JSPアクセスログ出力先(log/log_$(yyyyMMdd).txt)
263         * SERVER_INFO     : サーバー情報     [例: HN50G5 ( 200.1.50.165 ) ]
264         * SERVLET_INFO    : サーブレット情報 [例: Apache Tomcat/5.5.9     ]
265         * TOMCAT_WORK     : Tomcatワークの位置 [例: H:\java\tomcat5.5.17\work\Catalina\localhost\ver4  ]
266         * TOMCAT_HOME     : Tomcat環境の位置   [例: H:\java\tomcat5.5.17  ]
267         * JAVA_HOME       : Java実行環境の位置 [例: H:\java\jdk150\jre ]
268         * ENGINE_INFO     : バージョン番号 [例: 4.3.6.6 ]
269         *
270         * RESOURCE_DBID   : "RESOURCE" 固定値を設定。WEB-INF/web.xml で指定しても無効です。
271         *
272         * @og.rev 4.1.0.0 (2007/12/27) web.xmlからTOMCAT_PORTを読む処理を追加
273         * @og.rev 4.2.0.0 (2008/02/18) TOMCAT_PORTを環境変数から取得するよう変更
274         * @og.rev 4.2.0.0 (2008/02/20) web.xmlでSYSTEM_IDが空白の場合に大文字コンテキスト名が設定されるよう変更
275         * @og.rev 4.3.6.6 (2009/05/15) コンテキスト単位にエンジンバージョン情報を持つ(バージョンアップ判定用)
276         * @og.rev 5.6.7.1 (2013/08/09) RESOURCE_DBID の値に、"RESOURCE" を設定しておきます。
277         * @og.rev 5.6.7.3 (2013/08/23) TOMCAT_HOME を追加
278         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
279         * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、HybsSystem経由で、取得する。
280         * @og.rev 6.4.3.2 (2016/02/19) returnをやめて、引数のMapに直接値を設定します。
281         * @og.rev 6.9.0.1 (2018/02/05) RequestParam で設定される値を、できるだけ構築しておきます。
282         *
283         * @param map 読み書きするMapオブジェクト
284         * @param context Servletコンテキスト
285         * @param contextName コンテキスト名
286         *
287         */
288        private static void loadInitialParameter( final Map<String,String> map,final ServletContext context,final String contextName ) {
289                // コンテキストの初期化パラメータ他の情報を登録しておきます。
290                final Enumeration<?> enume = context.getInitParameterNames() ;          // 4.3.3.6 (2008/11/15) Generics警告対応
291                while( enume.hasMoreElements() ) {
292                        final String key = (String)enume.nextElement();
293                        String val = context.getInitParameter( key );
294                        if( val != null && val.isEmpty() ) { val = null; }
295                        map.put( key,val );
296                }
297
298                // SYSTEM_IDがnullの場合は大文字のコンテキスト名を設定
299                if( map.get( "SYSTEM_ID" ) == null ){ // 4.2.0.0 (2008/02/20)
300                        map.put( "SYSTEM_ID", contextName.toUpperCase( Locale.JAPAN ) );
301                }
302
303                // 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、HybsSystem経由で、取得する。
304                HybsSystem.setRealPath( context.getRealPath( "" ) );
305
306                // 各種システム情報を登録しておきます。
307                map.put( "REAL_PATH"    ,HybsSystem.getRealPath() );                                    // 6.2.4.1 (2015/05/22)
308                map.put( "CONTEXT_NAME" ,contextName );
309                map.put( "JSP"                  ,"/" + contextName + "/jsp" );
310                map.put( "SERVLET_INFO" ,context.getServerInfo() );
311                map.put( "TOMCAT_WORK"  ,String.valueOf( context.getAttribute( "javax.servlet.context.tempdir" ) ) );
312                map.put( "TOMCAT_HOME"  ,System.getProperty( "catalina.home" ) );               // 5.6.7.3 (2013/08/23)
313                map.put( "JAVA_HOME"    ,System.getProperty( "java.home" ) );
314
315                map.put( "HOST_NAME"    ,HybsSystem.HOST_NAME  );
316                map.put( "HOST_ADRS"    ,HybsSystem.HOST_ADRS  );
317                map.put( "SERVER_INFO"  ,HybsSystem.HOST_NAME  + " ( " + HybsSystem.HOST_ADRS + " )" );
318                map.put( "ENGINE_INFO"  ,BuildNumber.ENGINE_INFO );
319                // 5.6.7.1 (2013/08/09) RESOURCE_DBID の値に、"RESOURCE" を設定しておきます。
320                map.put( "RESOURCE_DBID", "RESOURCE" );
321
322                // 6.3.9.1 (2015/11/27) final化にともない、ロジック整理
323                final String TOMCAT_PORT = StringUtil.nval( System.getenv( "CONNECTOR_PORT" ) , map.get( "TOMCAT_PORT" ) );
324
325                // 6.3.9.1 (2015/11/27) final化にともない、ロジック整理
326                final String HOST_URL = TOMCAT_PORT == null || TOMCAT_PORT.isEmpty()
327                                                                        ? "**" : HybsSystem.HOST_NAME + ":" + TOMCAT_PORT + "/" + contextName + "/";
328                map.put( "HOST_URL", HOST_URL );
329
330                // 6.9.0.1 (2018/02/05) RequestParam で設定される値を、できるだけ構築しておきます。
331                map.put( "SERVER_NAME"  , "localhost" );                                                // HybsSystem.HOST_NAME でも良いかも。
332                map.put( "SERVER_URL"   , "http://localhost:" + TOMCAT_PORT + "/" );
333                map.put( "CONTEXT_URL"  , "http://localhost:" + TOMCAT_PORT + "/" + contextName + "/" );
334
335                System.out.println( "    Load Initial Parameter [" + map.size() + "] finished." );
336        }
337
338        /**
339         * アプリケーション個別に設定しているリソースDB(GE12)を取得します。
340         *
341         * データベースへの接続は、WEB-INF の DBConfig.xml で設定された情報を元に、
342         * org.opengion.fukurou.db.ConnectionFactory で接続先を取得します。
343         * ここでは、web.xml で定義された各アプリケーション個別のパラメ―タを取得します。
344         * SYSTEM_ID(必須) です。
345         *
346         * @og.rev 4.0.0.0 (2007/10/10) 接続先情報の管理見直し(コンテキスト初期設定)
347         * @og.rev 4.0.0.0 (2007/10/26) コネクションをファクトリーから取ってくるように変更
348         * @og.rev 4.3.6.5 (2009/05/08) dataパス内のXMLファイルも読み取るようにする
349         * @og.rev 4.3.6.6 (2009/05/15) ↑を廃止。自動インストール対応。
350         * @og.rev 5.1.2.0 (2010/01/01) connection.setAutoCommit は、ConnectionFactory で設定済みなので、コメントアウト
351         * @og.rev 5.1.9.0 (2010/08/01) 自動インストールの設定見直し(画面からのインストール対応)
352         * @og.rev 5.5.4.4 (2012/07/20) SQLException は、catch しているので、このメソッドからは、throws されない。
353         * @og.rev 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。
354         * @og.rev 5.6.6.1 (2013/07/12) xml パス内のXMLファイルがあれば、DB登録します。
355         * @og.rev 5.6.7.0 (2013/07/27) InitFileLoader で、resource以下は、DBID=RESOURCE xml以下は、DBID=DEFAULT に登録します。
356         * @og.rev 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
357         * @og.rev 5.7.2.0 (2014/01/10) RuntimeException は、catch しないようにします。
358         * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。
359         * @og.rev 6.4.3.2 (2016/02/19) returnをやめて、引数のMapに直接値を設定します。
360         * @og.rev 6.4.7.1 (2016/06/17) セキュリティの関連で、ログの詳細を出していませんでしたが、出すように変更します。
361         * @og.rev 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズを設定。
362         *
363         * @param       sysParam        入力システムパラメータマップ
364         */
365        private static void loadDBResource( final Map<String,String> sysParam ) {
366                final String SYSTEM_ID          = sysParam.get( "SYSTEM_ID" );
367
368                // 必須項目チェックを行います。SYSTEM_IDは必須です。
369                // これは、web.xml で定義が必要です。
370                // 4.0.0.0 (2007/10/23)接続情報XML化につきDB_URLチェックを削除
371                if( SYSTEM_ID == null || SYSTEM_ID.isEmpty() ) {
372                        final String errMsg = "システムパラメータの必須項目(SYSTEM_ID,DB_URL)が null です。" + CR
373                                                        + "SYSTEM_ID=[" + SYSTEM_ID + "] " + CR
374                                                        + "Versino=[" + BuildNumber.VERSION_NO + "] " + CR ;
375                        ERR_MSG_LIST.add( errMsg );
376                        return ;                        // 6.4.3.2 (2016/02/19)
377                }
378
379                Connection                      defConn         = null;                 // 5.6.7.0 (2013/07/27) DBID=DEFAULT  のコネクション
380                Connection                      rscConn         = null;                 // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
381                boolean                         errFlag         = true;
382                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
383                final String CONTEXT_NAME       = sysParam.get( "CONTEXT_NAME" );       // コンテキスト別システムリソース
384                final String HOST_URL           = sysParam.get( "HOST_URL" );           // 4.1.0.0 (2007/12/21) TOMCATへのアクセス用
385                final String RESOURCE_DBID      = sysParam.get( "RESOURCE_DBID" );      // 5.5.4.5 (2012/07/27) 初期起動時のDB接続先
386                try {
387
388                        // 4.0.0.0(2007/10/25)ConnectionをConnectionFactory経由で取得するように変更する。
389                        // コンテキスト名で接続しにいく。ApplicationInfoは使わないのでnull
390                        ConnectionFactory.init( CONTEXT_NAME, null ); // ConnectionFactoryの初期化
391
392                        defConn = ConnectionFactory.connection( null, null );                   // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
393                        rscConn = ConnectionFactory.connection( RESOURCE_DBID, null );  // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
394
395                        // 4.3.6.6 (2009/05/15)
396                        // 5.1.9.0 (2010/08/01) 自動インストールの設定見直し(画面からのインストール対応)
397                        final SystemInstaller installer = new SystemInstaller( defConn , rscConn , new PrintWriter( System.out, true ) );
398                        installer.autoInsUpd( SYSTEM_ID, CONTEXT_NAME, HOST_URL );
399
400                        // resource パス内のXMLファイルがあれば、先にDB登録します。(DBID=RESOURCE)
401                        // 6.0.0.0 (2014/04/11) XMLFileLoader に変更。
402                        XMLFileLoader loader = new XMLFileLoader( rscConn,true );       // リソースコネクションとuseTimeStamp=true 指定
403                        loader.loadClassPathFiles( "resource" ) ;
404
405                        // 5.6.6.1 (2013/07/12) xml パス内のXMLファイルがあれば、DB登録します。
406                        // 6.0.0.0 (2014/04/11) XMLFileLoader に変更。
407                        loader = new XMLFileLoader( defConn,true );                                     // デフォルトコネクションとuseTimeStamp=true 指定
408                        loader.loadClassPathFiles( "xml" ) ;
409
410                        // コンテキスト単位のシステムパラメータを GE12 に設定します。
411                        // dbXMLResourceInsert の後に登録する必要があります。
412                        dbResourceUpdate( rscConn,sysParam );                   // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
413                        rscConn.commit();
414        //              Closer.commit( rscConn );
415
416                        final Map<String,String> userMap = new HashMap<>(100);
417                        // 6.4.2.1 (2016/02/05) try-with-resources 文
418                        try( final PreparedStatement pstmt = rscConn.prepareStatement( QUERY ) ) {
419                                pstmt.setFetchSize( DB_FETCH_SIZE );                            // 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ
420                                pstmt.setString( 1,SYSTEM_ID );
421                                pstmt.setString( 2,HOST_URL ); // 4.1.0.0 (2007/12/21)
422                                try( final ResultSet resultSet = pstmt.executeQuery() ) {
423
424                                        while( resultSet.next() ) {
425                                                final String key = resultSet.getString(1);
426                                                final String val = resultSet.getString(2);
427                                                // 6.4.3.2 (2016/02/19) null値の登録はやめます。
428                                                userMap.put( key,val == null ? "" : val );              // 6.4.3.2 (2016/02/19)
429                                        }
430                                }
431                        }
432
433                        // DBを検索して、ユーザー設定リソース情報を取得します。
434
435                        System.out.println( "    Load DB Resource [" + userMap.size() + "] finished." );
436                        // リソースをマージします。
437                        sysParam.putAll( userMap );
438                        errFlag = false;        // エラーでない
439                }
440                catch( final SQLException ex ) {
441                        Closer.rollback( defConn );                     // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
442                        Closer.rollback( rscConn );                     // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
443                        LogWriter.log( ex );
444                        final String errMsg = ex.getMessage() + ":" + ex.getSQLState() ;
445                        ERR_MSG_LIST.add( errMsg );
446                }
447                catch( final UnsupportedEncodingException ex ) {
448                        Closer.rollback( defConn );                     // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
449                        Closer.rollback( rscConn );                     // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
450                        LogWriter.log( ex );
451                        final String errMsg = "UTF-8 がサポートされていない Java VM は、正規VMではありません。"
452                                                        + ex.getMessage();
453                        ERR_MSG_LIST.add( errMsg );
454                }
455                catch( final RuntimeException ex ) {
456                        Closer.rollback( defConn );                     // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
457                        Closer.rollback( rscConn );                     // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
458                        // 5.7.2.0 (2014/01/10) RuntimeException は、catch しないようにします。
459                        // 6.4.7.1 (2016/06/17)
460                        final String errMsg = "システムパラメータの設定で、エラーが発生しました。"
461                                                        + ex.getMessage();
462                        ERR_MSG_LIST.add( errMsg );
463
464                        throw new HybsSystemException( ex );            // 6.4.3.2 (2016/02/19) 生のRuntimeExceptionをラップしてthrowする。
465                }
466                finally {
467                        if( errFlag ) {
468                                ConnectionFactory.remove( defConn, null );                              // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
469                                ConnectionFactory.remove( rscConn, RESOURCE_DBID );             // 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
470                        }
471                        else {
472                                ConnectionFactory.close( defConn, null );                               // 5.6.7.0 (2013/07/27) DBID=DEFAULT のコネクション
473                                ConnectionFactory.close( rscConn, RESOURCE_DBID );              // 5.6.7.3 (2013/08/23) DBID=RESOURCE 漏れ
474                        }
475                }
476        }
477
478        /**
479         * エンジン内部定義の初期リソース情報をDB(GE12)に登録します。
480         *
481         * 初期リソース情報は、KBSAKU='0' で登録されている情報で、一旦すべて削除
482         * してから、全てのリソース情報を追加するという形をとります。
483         * ただし、属性情報(名称や概要など)を別途登録する場合は、全てを
484         * 削除せずに、UPDATE する方向で検討したいと思います。
485         * なお、この情報をDB登録する理由は、リソースの設定値を変えたい場合に、
486         * キーが判らない(JavaDOCからしか読み取れない)のでは不便な為に
487         * 用意しておくだけで、内部では SystemData オブジェクトとして定義
488         * されている値を使用するため、このデータベース値は、使用していません。
489         *
490         * @param       conn    登録用コネクション(リソース用)
491         * @param       map             入力システムパラメータマップ
492         * @throws      SQLException データベースアクセスエラー
493         */
494        private static void dbResourceUpdate( final Connection conn,final Map<String,String> map )
495                                throws SQLException {
496
497                final String systemId = map.get( "SYSTEM_ID" );
498                final String HOST_URL = map.get( "HOST_URL" ); // 4.1.0.0 (2007/12/21)
499
500                // 既存の設定値を全件DELETEします。
501                int delCnt;
502                PreparedStatement pstmt = null;
503                try {
504                        pstmt = conn.prepareStatement( DEL_SYS );
505                        pstmt.setString( 1, systemId );
506                        pstmt.setString( 2, HOST_URL ); // 4.1.0.0 (2007/12/21)
507                        delCnt = pstmt.executeUpdate();
508                }
509                finally {
510                        Closer.stmtClose( pstmt );
511                }
512
513                // 新設定値を全件INSERTします。
514                final Set<String> keyset = map.keySet();
515                final String[] keys = keyset.toArray( new String[keyset.size()] );
516
517                int insCnt = 0;
518                try {
519                        pstmt = conn.prepareStatement( INS_SYS );
520                        for( int i=0; i<keys.length; i++ ) {
521                                pstmt.setString( 1,systemId );
522                                pstmt.setString( 2,HOST_URL);
523                                pstmt.setString( 3,keys[i] );
524                                pstmt.setInt( 4,( i + 1 ) * 10 );
525                                pstmt.setString( 5,map.get( keys[i] ) );
526                                pstmt.setString( 6,keys[i] );
527                                insCnt += pstmt.executeUpdate();
528                        }
529                }
530                finally {
531                        Closer.stmtClose( pstmt );
532                }
533
534                System.out.print( "    DB Context Resource Reconfiguration " );
535                System.out.println( "DELETE=[" + delCnt + "],INSERT=[" + insCnt + "] finished." );
536        }
537
538        /**
539         *  ServletContext の名称を取得します。
540         *
541         * コンテキストのアクセスされたパス( /training など )の名称を、
542         * 取得します。(アクセス先の仮想フォルダ名)
543         * 以前は、配備記述子(WEB-INF/web.xml)の display-name 要素を見て、
544         * 無ければ、実フォルダ名を返していました。
545         * ※ warファイルのバージョン管理(セッション管理)対応として、## 以下の文字列を削除します。
546         *    ## は、war化の際に、"%23%23" に変換されるため、それも取り除き処理の対象とします。
547         *
548         * @og.rev 6.6.0.0 (2016/12/01) コンテキストパスから、##バージョン番号を取り去った値を返すようにします。
549         *
550         * @param  context Servletコンテキスト
551         *
552         * @return コンテキストのコンテキスト名
553         */
554        private static String getContextName( final ServletContext context ) {
555                String name = null;
556                try {
557                        final String path = context.getResource( "/" ).getFile();
558                        final int st = path.lastIndexOf( '/',path.length()-2 );         // 後ろから '/' を検索した位置(一番後ろは、'/' が付いているので注意)
559                        int ed = path.indexOf( "##",st+1 );                                                     // '/' 以降で、"##" を検索した位置
560                        if( ed < 0 ) {
561                                ed = path.indexOf( "%23%23",st+1 );                                             // '/' 以降で、"%23%23" を検索した位置
562                        }
563                        final int ed2 = ed > 0 ? ed : path.length()-1 ;                         // ## が無ければ、一番最後の文字を含まない、一つ前まで。あれば、そこまで
564                        name = path.substring( st+1,ed2 );
565                }
566                catch( final MalformedURLException ex ) {
567                        LogWriter.log( ex );
568                        final String errMsg = "このパス名は、正しいフォームではありません。 "
569                                                + ex.getMessage();
570                        ERR_MSG_LIST.add( errMsg );
571                }
572                return name ;
573        }
574
575        /**
576         * 指定のクラスの public static なフィールドキーと値のマップを作成します。
577         * 主に、エンジン関連のクラスにパラメータファイルをクラスとして定義し、
578         * エンジンとともに配布します。配布されたクラスを元に、パラメータを
579         * 読み取ります。
580         * この処理は リフレクションを使用してクラスの public static フィールドを
581         * 取得し、LinkedHashMap により、取得順をキープしたまま、Mapを返します。
582         *
583         * @og.rev 5.7.2.0 (2014/01/10) ERR_MSG_LIST は、一旦文字列に変換してから追加します。
584         * @og.rev 6.4.3.2 (2016/02/19) returnをやめて、引数のMapに直接値を設定します。
585         *
586         * @param  map   読み書きするMapオブジェクト
587         * @param  cls   クラスオブジェクト
588         */
589        private static void loadParameter( final Map<String,String> map , final Class<?> cls ) {
590
591                int cnt = 0;
592                try {
593                //      final Field[] field = cls.getFields(); の取得。public フィールドのみ
594                        for( final Field fld : cls.getFields() ) {
595                                if( Modifier.isStatic( fld.getModifiers() ) ) {
596                                        map.put( fld.getName() , (String)fld.get( null ) );
597                                        cnt++ ;
598                                }
599                        }
600
601                }
602                catch( final IllegalAccessException ex ) {
603                        LogWriter.log( ex );
604                        // 5.7.2.0 (2014/01/10) ERR_MSG_LIST は、一旦文字列に変換してから追加します。
605                        final String errMsg = "クラスから、パラメータを取得できませんでした。" + CR
606                                                                + "  クラス名=[" + cls.getName() + "]"                              + CR
607                                                                + ex.getMessage();
608                        ERR_MSG_LIST.add( errMsg );
609                }
610
611                System.out.println( "    ClassLoad " + cls.getName() + " Parameter [" + cnt + "] finished." );
612
613        }
614
615        /**
616         * 指定のキーワードのファイルをクラスパスより取得し、キーと値のマップを作成します。
617         * 主に、エンジン関連のクラスにパラメータファイルをPlugInクラスとして定義し、配布します。
618         * この処理の取得に、クラスパスの順序が関係します。最初に取得されたキーは、あとから
619         * 読み取られたクラスパスは、再セットしません。
620         *
621         * @og.rev 5.3.6.0 (2011/06/01) 並び順を、キーの名称順とする。
622         * @og.rev 5.7.2.0 (2014/01/10) ERR_MSG_LIST は、一旦文字列に変換してから追加します。
623         * @og.rev 5.7.2.0 (2014/01/10) RuntimeException は、catch しないようにします。
624         * @og.rev 6.4.3.2 (2016/02/19) returnをやめて、引数のMapに直接値を設定します。
625         * @og.rev 6.6.0.0 (2016/12/01) コンテキストパスから、##バージョン番号を取り去った値を返すようにします。
626         *
627         * @param       ioMap   読み書きするMapオブジェクト
628         * @param       keyword クラスオブジェクトを検索する元
629         */
630        private static void loadParameter( final Map<String,String> ioMap , final String keyword ) {
631                final Map<String,String> map = new TreeMap<>();         // 5.3.6.0 (2011/06/01) 並び順を、キーの名称順とする。
632                try {
633                        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
634                        final Enumeration<URL> enume = loader.getResources( keyword );                  // 4.3.3.6 (2008/11/15) Generics警告対応
635                        while( enume != null && enume.hasMoreElements() ) {
636                                final URL url = enume.nextElement();            // 4.3.3.6 (2008/11/15) Generics警告対応
637                                // jar:file:/実ディレクトリ または、file:/実ディレクトリ
638                                final String dir = url.getFile().replaceAll( "%23%23","##" );           // 6.6.0.0 (2016/12/01)
639
640                                final FindClassFiles filenames = new FindClassFiles( dir,keyword );
641                                final String[] names = filenames.getFilenames();
642                                for( int i=0; i<names.length; i++ ) {
643                                        final String val = names[i];
644                                        final String key = val.substring( val.lastIndexOf( '.' )+1 );
645                                        if( key.indexOf( '_' ) >= 0 && !map.containsKey( key ) && key.indexOf( '$' ) < 0 ) {
646                                                map.put( key , val );
647                                        }
648                                }
649                                System.out.println( "    FileCheck " + dir + " [" + names.length + "] find." );
650                        }
651                }
652                catch( final IOException ex ) {
653                        LogWriter.log( ex );
654                        // 5.7.2.0 (2014/01/10) ERR_MSG_LIST は、一旦文字列に変換してから追加します。
655                        final String errMsg = "キーワードから、パラメータを取得できませんでした。"       + CR
656                                                + "  キーワード名=[" + keyword + "]"                                          + CR
657                                                + ex.getMessage();
658                        ERR_MSG_LIST.add( errMsg );
659                }
660                // 5.7.2.0 (2014/01/10) RuntimeException は、catch しないようにします。
661                System.out.println( "    FileLoad " + keyword + " Parameter [" + map.size() + "] finished." );
662                ioMap.putAll( map );            // TreeMap で、キーの名称順に並んだMapを、LinkedHashMapに登録順に設定する。
663        }
664}