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.BufferedReader;
019import java.io.File;
020// import java.io.FileFilter;                                                   // 7.0.1.4 (2018/11/26)
021import java.io.InputStream;
022import java.io.InputStreamReader;
023import java.io.PrintWriter;
024import java.io.Reader;
025import java.io.UnsupportedEncodingException;
026import java.io.IOException;                                                             // 8.5.4.2 (2024/01/12)
027
028import java.sql.Connection;
029import java.sql.PreparedStatement;
030import java.sql.ResultSet;
031import java.sql.SQLException;
032import java.sql.Statement;
033import java.util.ArrayList;
034import java.util.List;
035import java.util.Locale;
036
037import org.opengion.fukurou.system.BuildNumber;                 // 6.4.2.0 (2016/01/29) hayabusa.common.BuildNumber → fukurou.system.BuildNumber に移動
038import org.opengion.fukurou.system.Closer;
039import org.opengion.fukurou.system.HybsConst;                   // 7.2.3.1 (2020/04/17)
040import org.opengion.fukurou.util.FileUtil;
041import org.opengion.fukurou.util.StringUtil;
042import org.opengion.fukurou.util.ZipArchive;                    // 6.0.0.0 (2014/04/11) ZIP API変更
043import org.opengion.fukurou.xml.HybsXMLSave;
044import org.opengion.fukurou.db.DBUtil;
045
046/**
047 * システムの自動インストールと自動更新を行います。
048 *
049 * (1)初期インストール・自動更新(#autoInsUpd)
050 *   ①初期自動インストールを行うには、起動時の環境変数にINSTALL_CONTEXTSが
051 *     設定されている必要があります。
052 *     この環境変数が設定されている場合、システムリソーステーブル(GE12)が存在しなければ、
053 *     エンジンがインストールされていないと判断し、自動インストールを行います。
054 *     INSTALL_CONTEXTSにge,gfが指定されている場合は、開発環境を含めたフルバージョンが
055 *     インストールされます。
056 *     geのみが指定されている場合は、コアモジュールであるgeのみがインストールされます。
057 *
058 *     インストールスクリプトは、
059 *      webapps/[CONTEXT]/db/[DBNAME]/xml/install                       DBID=DEFAULT
060 *      webapps/[CONTEXT]/db/[DBNAME]/xml/update                        DBID=DEFAULT
061 *     以下にあるXMLファイルが全て実行されます。
062 *     また、同時に
063 *      webapps/[CONTEXT]/db/common/xml/install                         DBID=DEFAULT
064 *      webapps/[CONTEXT]/db/common/xml/update                          DBID=DEFAULT
065 *      webapps/[CONTEXT]/db/resource/xml/install                       DBID=RESOURCE   (5.6.7.0 (2013/07/27) 追加)
066 *      webapps/[CONTEXT]/db/resource/xml/update                        DBID=RESOURCE   (5.6.7.0 (2013/07/27) 追加)
067 *     以下にあるデータロードスクリプトも全て実行されます。
068 *
069 *   ②自動更新については、システムリソーステーブル(GE12)の更新と、各システムの更新の2つがあります。
070 *     GE12更新の判断基準は、システムID='**'に格納されているバージョン(同一のGE12を使用し
071 *     ているシステムの最大バージョン番号)がアップした場合です。
072 *     この場合に、エンジン内部で保持しているXMLファイルよりシステムリソースの再ロードを行います。
073 *     各システムの更新の判断基準は、システムID=各システムのバージョン番号がアップされた場合です。
074 *
075 *     更新スクリプトは、
076 *      webapps/[CONTEXT]/db/[DBNAME]/xml/update                        DBID=DEFAULT
077 *     以下にあるXMLファイルが全て実行されます。
078 *     また、同時に
079 *      webapps/[CONTEXT]/db/common/xml/update                          DBID=DEFAULT
080 *      webapps/[CONTEXT]/db/resource/xml/update                        DBID=RESOURCE   (5.6.7.0 (2013/07/27) 追加)
081 *     以下にあるデータロードスクリプトも全て実行されます。
082 *
083 * (2)インストール(#install)
084 *  自動インストールは、通常は画面からコンテキストのアーカイブを指定して行います。
085 *
086 *  アーカイブの内容としては、アーカイブの直下がコンテキスト名のフォルダとなっている必要があります。
087 *  このコンテキストフォルダをwebapps以下に展開します。
088 *
089 *  また、Tomcatのコンテキストの設定ファイル、([CONTEXT].xml)が"WEB-INFの直下を配置している必要があります。
090 *
091 *  このインストールでは、Tomcatに対するコンテキスト定義のXMLファイルの配備及び、
092 *  各種DB、データのロードを行います。
093 *
094 *  インストールスクリプトは、
095 *   webapps/[CONTEXT]/db/[DBNAME]/xml/install                          DBID=DEFAULT
096 *  以下にあるXMLファイルが全て実行されます。
097 *  また、同時に
098 *   webapps/[CONTEXT]/db/common/xml/install                            DBID=DEFAULT
099 *   webapps/[CONTEXT]/db/resource/xml/install                          DBID=RESOURCE   (5.6.7.0 (2013/07/27) 追加)
100 *  以下にあるデータロードスクリプトも全て実行されます。
101 *
102 * @og.rev 4.3.6.6 (2009/05/15) 新規作成
103 * @og.rev 7.0.2.3 (2019/04/01) xmlファイルのタイムスタンプによる更新制御を入れます。(保留)
104 * @og.group 初期化
105 *
106 * @version  4.0
107 * @author   Hiroki Nakamura
108 * @since    JDK5.0,
109 */
110public final class SystemInstaller {
111        private final Connection defConn;               // 5.6.7.0 (2013/07/27) DBID=DEFAULT  のコネクション
112        private final Connection rscConn;               // 5.6.7.0 (2013/07/27) DBID=RESOURCE のコネクション
113        private final PrintWriter out;                  // 5.1.9.0 (2010/08/01)
114        private final String DBNAME;                    // 5.5.4.4 (2012/07/20) 共通化 (DBID=DEFAULT のDB名)
115
116        /** エンジン共通パラメータ(SYSTEM_ID='**' KBSAKU='0')のXML ファイルの指定  {@value}        */
117        public static final String GE12_XML = "org/opengion/hayabusa/common/GE12.xml";
118
119        /** エンジン共通パラメータ(SYSTEM_ID='**' KBSAKU='0')のENGINE_INFO 読み取りクエリー {@value}        */
120        public static final String SEL_MAX_ENG = "select PARAM from GE12"
121                                                                                                + " where SYSTEM_ID='**'"
122                                                                                                + " and PARAM_ID='ENGINE_INFO' and KBSAKU='0' and FGJ='1'" ;
123
124        /** エンジン個別(SYSTEM_ID='個別' KBSAKU='0'  CONTXT_PATH='自身')のバージョン情報を取得するクエリー {@value} 4.3.6.6 (2009/05/15) */
125        public static final String SEL_SYS_ENG = "select PARAM from GE12"
126                                                                                                + " where SYSTEM_ID=? and CONTXT_PATH=?"
127                                                                                                + " and PARAM_ID='ENGINE_INFO' and KBSAKU='0' and FGJ='1'" ;
128
129        private static final String FS                  = File.separator ;                                              // 5.5.4.4 (2012/07/20) static化
130//      private static final String APP_BASE    = System.getenv( "APP_BASE" ) + FS;             // 5.5.4.4 (2012/07/20) static化
131//      private static final String APP_BASE    = StringUtil.nval(System.getenv( "APP_BASE" ),System.getProperty( "APP_BASE" )) + FS; // 5.9.30.2 (2018/03/23)
132        private static final String APP_BASE    = HybsConst.getenv( "APP_BASE" ) + FS; // 7.2.3.1 (2020/04/17)
133
134//      // 7.0.1.4 (2018/11/26) FileFilter を利用して、フォルダの絞り込みを行う。
135//      private static final FileFilter INS_FLTR = file -> {
136//                                                                                                      if( file.isFile() ) { return true; }
137//                                                                                                      final String name = file.getName().toUpperCase( Locale.JAPAN );
138//                                                                                                      return file.isDirectory() && ( name.contains( "INSTALL" ) ||  name.contains( "UPDATE" ) );
139//                                                                                              };
140//
141//      private static final FileFilter UPD_FLTR = file -> {
142//                                                                                                      if( file.isFile() ) { return true; }
143//                                                                                                      final String name = file.getName().toUpperCase( Locale.JAPAN );
144//                                                                                                      return file.isDirectory() && name.contains( "UPDATE" );
145//                                                                                              };
146
147        /**
148         * データベース処理をおこなうに当たり、処理のタイプを指定するための、enum 定義です。
149         * 文字列で扱っていた箇所を、enum と置き換えます。
150         *
151         * @og.rev 5.5.4.4 (2012/07/20) 新規追加
152         */
153        private enum EXEC_TYPE { INSTALL , UPDATE }             // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessarySemicolon
154//      private static enum EXEC_TYPE { INSTALL , UPDATE } ;
155
156//      /**
157//       * データベース処理をおこなうに当たり、処理のタイプを指定するための、enum 定義です。
158//       * 文字列で扱っていた箇所を、enum と置き換えます。
159//       *
160//       * @og.rev 5.5.4.4 (2012/07/20) 新規追加
161//       * @og.rev 7.0.1.4 (2018/11/26) FileFilter を利用して、フォルダの絞り込みを行う。
162//       */
163//      private static enum EXEC_TYPE {
164//              INSTALL( INS_FLTR ) ,
165//              UPDATE(  UPD_FLTR ) ;
166//
167//              private final FileFilter filter ;
168//
169//              /** コンストラクター
170//               *
171//               * @param filter ファイルの絞込みに使用するフィルター
172//               */
173//              private EXEC_TYPE( final FileFilter filter ) { this.filter = filter; }
174//
175//              /** ファイルの絞込みに使用するフィルターを返します。
176//               *
177//               * @return ファイルの絞込みに使用するフィルター
178//               */
179//              public FileFilter getFilter() { return filter; }
180//      } ;
181
182        /**
183         * システムインストール・更新クラスのコンストラクタです
184         *
185         * なお、このクラスの中の処理で、エラーが発生しても、Connection は、close 等しません。
186         * 呼び出し元で、try ~ finally で、処理してください。
187         *
188         * @og.rev 5.5.4.4 (2012/07/20) VERSIONは、直接 BuildNumber.ENGINE_INFO を使用。
189         * @og.rev 5.6.7.0 (2013/07/27) アプリケーション登録用とリソース登録用のコネクションを分ける
190         * @og.rev 5.9.24.1 (2017/09/08) DB名を出力する
191         *
192         * @param       defConn アプリケーション登録用コネクション
193         * @param       rscConn リソース登録用コネクション
194         * @param       out     表示用のWriter
195         */
196        public SystemInstaller( final Connection defConn, final Connection rscConn, final PrintWriter out ) {
197                this.defConn = defConn;                 // 5.6.7.0 (2013/07/27) アプリケーション登録用
198                this.rscConn = rscConn;                 // 5.6.7.0 (2013/07/27) リソース登録用
199                this.out = out;
200
201                // 5.6.7.0 (2013/07/27) ProductName は、DBUtil 経由で取得する。
202                DBNAME = DBUtil.getProductName( defConn );                                              // 5.6.7.0 (2013/07/27) DBID=DEFAULT  のDB名
203                out.println( "    Database Information ( " + DBNAME + " )" );   // 5.9.24.1
204        }
205
206        /**
207         * システムの初期自動インストール・自動更新を行います。
208         *
209         * 詳細は、クラスドキュメントを参照して下さい。
210         *
211         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
212         * @og.rev 5.5.4.4 (2012/07/20) VERSIONは、直接 BuildNumber.ENGINE_INFO を使用。
213         * @og.rev 7.0.1.4 (2018/11/26) 初期自動インストール(oldMaxVersion == "none")時でも、loadXMLScript(Update)とdbXMLResourceInsert は行う
214         *
215         * @param systemId システムID
216         * @param context コンテキスト名
217         * @param hostUrl ホスト文字列
218         * @throws UnsupportedEncodingException エンコード名 "UTF-8" が存在しなかった場合。
219         * @see   #dbXMLResourceInsert()
220         */
221        public void autoInsUpd( final String systemId, final String context, final String hostUrl ) throws UnsupportedEncodingException  {
222                final String oldMaxVersion    = getOldMaxVersion();
223                final String oldSystemVersion = getOldSystemVersion( systemId, hostUrl );
224
225                out.println( "    System Version Information ( " + systemId + " )" );
226                out.println( "      Load Version [ " + BuildNumber.ENGINE_INFO + " ]" );                // 5.5.4.4 (2012/07/20)
227                out.println( "        -> Resource Version[ " + oldMaxVersion + " ]" );
228                out.println( "        -> System   Version[ " + oldSystemVersion + " ] " );
229
230                // 初期自動インストール
231                if( "none".equalsIgnoreCase( oldMaxVersion ) ) {
232                        out.println( "      !!! openGion ENVIROMENT IS NOT INSTALLED !!!" );
233
234//                      final String INSTALL_CONTEXTS = System.getenv( "INSTALL_CONTEXTS" );
235                        final String INSTALL_CONTEXTS = HybsConst.getenv( "INSTALL_CONTEXTS" );         // 7.2.3.1 (2020/04/17)
236                        if( INSTALL_CONTEXTS == null || INSTALL_CONTEXTS.isEmpty() ) {
237                                out.println( "        !!! \"INSTALL_CONTEXT\" IS NOT CONFIGURED\" !!!" );
238                                out.println( "        !!! \"SET ENRIVOMENT PARAMETER NAMED \"INSTALL_CONTEXT\" ON INIT_SCRIPT !!!" );
239//                              return;
240
241                                // 7.0.1.4 (2018/11/26) 初期自動インストール(oldMaxVersion == "none")時でも、loadXMLScript(Update)とdbXMLResourceInsert は行う
242                                out.println( "      Start Enviroment Update ( " + context + " )" );
243                                loadXMLScript( EXEC_TYPE.UPDATE , context );
244                                out.println( "      Completed               ( " + context + " )" );
245                        }
246                        else {
247                                out.println( "      Start Initiall Enviroment Install : install type ( " + INSTALL_CONTEXTS + " )" );
248                                // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
249//                              final String[] insSys = StringUtil.csv2Array( INSTALL_CONTEXTS );
250//                              for( int i=0; i<insSys.length; i++ ) {
251//                                      out.println( "        Install    ( " + insSys[i] + " )" );
252//                                      loadXMLScript( EXEC_TYPE.INSTALL, insSys[i] );
253//                                      out.println( "        Completed  ( " + insSys[i] + " )" );
254//                              }
255                                for( final String insCtx : StringUtil.csv2Array( INSTALL_CONTEXTS ) ) {
256                                        out.println( "        Install    ( " + insCtx + " )" );
257                                        loadXMLScript( EXEC_TYPE.INSTALL, insCtx );
258                                        out.println( "        Completed  ( " + insCtx + " )" );
259                                }
260                        }
261
262                        out.println( "      Start SystemParameter Reload" );
263                        dbXMLResourceInsert();
264                        out.println( "      Completed" );
265                }
266                // 自動更新
267                else {
268                        if( oldSystemVersion == null || oldSystemVersion.compareTo( BuildNumber.ENGINE_INFO ) < 0 ){            // 5.5.4.4 (2012/07/20)
269                                out.println( "      Start Enviroment Update ( " + context + " )" );
270                                loadXMLScript( EXEC_TYPE.UPDATE , context );
271                                out.println( "      Completed               ( " + context + " )" );
272                        }
273
274                        if( oldMaxVersion == null || oldMaxVersion.compareTo( BuildNumber.ENGINE_INFO ) < 0 ){                          // 5.5.4.4 (2012/07/20)
275                                out.println( "      Start SystemParameter Reload" );
276                                dbXMLResourceInsert();
277                                out.println( "      Completed" );
278                        }
279                }
280        }
281
282        /**
283         * システムの自動インストールを行います。
284         *
285         * 詳細は、クラスドキュメントを参照して下さい。
286         *
287         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
288         * @og.rev 5.5.4.4 (2012/07/20) FS , APP_BASE を、共通に設定
289         * @og.rev 6.0.0.0 (2014/04/11) ZIP API変更
290         * @og.rev 7.2.3.1 (2020/04/17) System.getenv → HybsConst.getenv 変更(サービス化対応)
291         *
292         * @param buildArchive コンテキストのアーカイブファイル
293         */
294        public void install( final File buildArchive ) {
295
296                out.println( "      Check Archive File and Enviroment" );
297
298                // アーカイブの存在チェック
299                if( !buildArchive.exists() ) {
300                        out.println( "        !!! Archive File does not exists File=[ " + buildArchive.getAbsolutePath() + "] !!!" );
301                        out.println( "        !!! Install Aborted !!! " );
302                        return;
303                }
304
305                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
306                final String tempDir    = HybsSystem.sys( "REAL_PATH" ) + HybsSystem.sys( "FILE_URL" ) + System.currentTimeMillis() + FS;
307//              final String ctxtXmlDir = System.getenv( "CATALINA_HOME" ) + FS + "conf" + FS + System.getenv( "ENGINE_NAME" ) + FS + "localhost" + FS;
308                final String ctxtXmlDir = HybsConst.getenv( "CATALINA_HOME" ) + FS + "conf" + FS + HybsConst.getenv( "ENGINE_NAME" ) + FS + "localhost" + FS;   // 7.2.3.1 (2020/04/17)
309
310                // アーカイブを一時ファイルに展開します。
311                ZipArchive.unCompress( new File( tempDir ), buildArchive );                             // 6.0.0.0 (2014/04/11) ZIP API変更
312
313                // アーカイブの内容チェック
314                final File[] ctxts = new File( tempDir ).listFiles();
315                // 6.3.9.0 (2015/11/06) null になっている可能性がある(findbugs)
316                if( ctxts != null ) {
317                        for( final File ctxt : ctxts ) {
318                                // 5.1.9.0 (2010/08/01) if の条件を入れ替えます。(Avoid if(x != y) ..; else ..;)
319                                final String context = ctxt.getName();
320                                if( ctxt.isDirectory() ) {
321                                        // アーカイブ中に[CONTEXT].xmlが存在していない場合はエラー(何も処理しない)
322                                        final File srcCtxtXml = new File( tempDir + context + FS + "WEB-INF" + FS + context + ".xml" );
323                                        if( !srcCtxtXml.exists() ) {
324                                                out.println( "        !!! Context XML Does not exists =[ " + srcCtxtXml.getAbsolutePath() + "] !!!" );
325                                                out.println( "        !!! Install Aborted !!! " );
326                                                return;
327                                        }
328
329                                        // [CONTEXT].xmlが既に存在している場合はエラー(何も処理しない)
330                                        final File ctxtXml = new File( ctxtXmlDir + context + ".xml" );
331                                        if( ctxtXml.exists() ) {
332                                                out.println( "        !!! Context XML File Already Installed File=[ " + ctxtXml.getAbsolutePath() + "] !!!" );
333                                                out.println( "        !!! Install Aborted !!! " );
334                                                return;
335                                        }
336
337                                        // webapps/[CONTEXT]が既に存在している場合はエラー(何も処理しない)
338                                        final File webAppsDir = new File( APP_BASE + context );
339                                        if( webAppsDir.exists() ) {
340                                                out.println( "        !!! Context Path Already Exists Path=[ " + webAppsDir.getAbsolutePath() + "] !!!" );
341                                                out.println( "        !!! Install Aborted !!! " );
342                                                return;
343                                        }
344
345                                        out.println( "        This Archive includes SYSTEM ( " + context + " ) for Install" );                  // 5.5.4.4 (2012/07/20)
346                                }
347                                // ファイルが含まれている場合はエラー(何も処理しない)
348                                else {
349                                        out.println( "        !!! This Archive is not Installer. Because include FILE not DIRECTORY. File=[ " + context + "] !!!" );    // 5.5.4.4 (2012/07/20)
350                                        out.println( "        !!! Install Aborted !!! " );
351                                        return;
352                                }
353                        }
354
355                        // アーカイブをコンテキストファイル以下にコピー
356                        for( final File ctxt : ctxts ) {
357                                final String context = ctxt.getName();
358                                out.println( "      Start Enviroment Install ( " + context + " )" );
359
360                                // コンテキストのファイルをコピーします。
361                                FileUtil.copyDirectry( tempDir + context, APP_BASE + context );
362
363                                // [CONTEXT].xmlをTomcatのconf以下に展開します。
364                                FileUtil.copy( tempDir + context + FS + "WEB-INF" + FS + context + ".xml", ctxtXmlDir + context + ".xml" );
365
366                                // DBスクリプトをロードします。
367                                loadXMLScript( EXEC_TYPE.INSTALL , context );
368                                out.println( "      Completed                ( " + context + " )" );
369                        }
370                }
371                out.println( "      Install Process All Completed." );
372        }
373
374        /**
375         * インストール、更新用のXMLスクリプトをロードします。
376         *
377         * @og.rev 5.0.0.2 (2009/09/15) .xmlファイル以外は読み込まないように修正
378         * @og.rev 5.1.1.0 (2009/12/01) コメントを出して、処理中ということが判る様にします。
379         * @og.rev 5.1.9.0 (2010/08/01) DB非依存の定義・データの読み込み対応
380         * @og.rev 5.5.4.4 (2012/07/20) FS , APP_BASE , DBNAME を、共通に設定
381         * @og.rev 5.6.7.0 (2013/07/27) アプリケーション登録用とリソース登録用のコネクションを分ける
382         * @og.rev 8.4.2.0 (2023/03/03) DBスクリプトなしエラー時のメッセージを少し和らげる。
383         *
384         * @param       type    更新タイプ[EXEC_TYPE.INSTALL/EXEC_TYPE.UPDATE]
385         * @param       context コンテキスト名
386         */
387        private void loadXMLScript( final EXEC_TYPE type, final String context ) {
388                // DB名からスクリプトを格納しているフォルダを探します。
389                final String scriptBase = APP_BASE + context.toLowerCase( Locale.JAPAN ) + FS + "db";
390                final File[] dbDir = new File( scriptBase ).listFiles();
391                if( dbDir == null || dbDir.length == 0 ) {
392                        out.println( "             DB Folder not found. [" + scriptBase + "]"  );
393                        return;
394                }
395
396                String scriptPath = null;
397                // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
398//              for( int i=0; i<dbDir.length; i++ ) {
399//                      if( DBNAME.indexOf( dbDir[i].getName() ) >= 0 ) {
400//                              scriptPath = dbDir[i].getAbsolutePath();
401//                              break;
402//                      }
403//              }
404                for( final File file : dbDir ) {
405                        if( DBNAME.indexOf( file.getName() ) >= 0 ) {
406                                scriptPath = file.getAbsolutePath();
407                                break;
408                        }
409                }
410                if( scriptPath == null ) {
411//                      out.println( "             !!! Script Folder for [ " + DBNAME + " ] not found !!!" );
412                        out.println( "             DB Script not found. [" + DBNAME + "]" );    // 8.4.2.0 (2023/03/03)
413                        return;
414                }
415
416                // webapps/[CONTEXT]/db/[DBNAME]/
417                execScripts( type , scriptPath , defConn );                                             // 5.6.7.0 (2013/07/27) DBID=DEFAULT に登録
418
419                // 5.1.9.0 (2010/08/01) DB非依存の定義・データの読み込み対応
420                // webapps/[CONTEXT]/db/common/
421                execScripts( type , scriptBase + FS + "common" , defConn );             // 5.6.7.0 (2013/07/27) DBID=DEFAULT に登録
422
423                // 5.6.7.0 (2013/07/27) DBID=RESOURCE に登録
424                // webapps/[CONTEXT]/db/resource/
425                execScripts( type , scriptBase + FS + "resource" , rscConn );   // 5.6.7.0 (2013/07/27) DBID=RESOURCE に登録
426        }
427
428        /**
429         * XMLファイルで定義されたDBスクリプトを実行します。
430         *
431         * 引数のtypeに応じて、処理するフォルダが異なります。
432         *   type=INSTALL の場合は、[scriptPath]/xml/install と、[scriptPath]/xml/update 以下の xml ファイル
433         *   type=それ以外の場合は、[scriptPath]/xml/update 以下の xml ファイル
434         * です。
435         *
436         * 現時点では、scriptPath には、下記の 3種類のアドレスが渡され、それぞれ、登録するコネクションが異なります。
437         *   webapps/[CONTEXT]/db/[DBNAME]/                     DBID=DEFAULT
438         *   webapps/[CONTEXT]/db/common/                       DBID=DEFAULT
439         *   webapps/[CONTEXT]/db/resource/                     DBID=RESOURCE
440         *
441         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
442         * @og.rev 5.5.4.4 (2012/07/20) FS , APP_BASE を、共通に設定
443         * @og.rev 5.5.8.4 (2012/11/22) firebird対応。フォルダ単位commitを行う
444         * @og.rev 5.6.7.0 (2013/07/27) Connection引数追加。リソースとアプリを切り分ける。
445         * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
446//       * @og.rev 7.0.1.4 (2018/11/26) FileFilter を利用して、フォルダの絞り込みを行う。
447//       * @og.rev 7.0.2.3 (2019/04/01) xmlファイルのタイムスタンプによる更新制御を入れます。(保留)
448         * @og.rev 7.0.6.1 (2019/10/11) xmlファイルのタイムスタンプによる更新制御を入れます。
449         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応
450         *
451         * @param       type            更新タイプ[EXEC_TYPE.INSTALL/EXEC_TYPE.UPDATE]
452         * @param       scriptPath      XMLファイルのあるパス
453         * @param       conn            コネクションオブジェクト
454         */
455        private void execScripts( final EXEC_TYPE type, final String scriptPath, final Connection conn ) {
456                // webapps/[CONTEXT]/db/[DBNAME]/xml/(install|update) 内のスクリプトを実行します
457                final List<String> list = new ArrayList<>();
458
459//              // 7.0.1.4 (2018/11/26) FileFilter を利用して、フォルダの絞り込みを行う。
460//              //                    対象フォルダのトップ                  フィルター         ソート   結果   isCopy(コピー中ファイルを含む)
461//              FileUtil.getFileList( new File( scriptPath  + FS + "xml" ), type.getFilter() , true, list , true );
462
463                if( type == EXEC_TYPE.INSTALL ) {
464                        FileUtil.getFileList( new File( scriptPath  + FS + "xml" + FS + "install" ), true, list );
465                        FileUtil.getFileList( new File( scriptPath  + FS + "xml" + FS + "update"  ), true, list );
466                }
467                else {
468                        FileUtil.getFileList( new File( scriptPath  + FS + "xml" + FS + "update"  ), true, list );
469
470                        /*******************************************************************************
471                         * updateの場合に、更新前のバージョンからの変更スクリプトを実行する機能が必要
472                         *******************************************************************************/
473                }
474
475                if( ! list.isEmpty() ) {
476                        String dir1 = null;             // 5.1.1.0 (2009/12/01)
477                        for( final String name : list ) {
478                                if( name.endsWith( ".xml" ) ) {         // 5.0.0.2 (2009/09/15)
479                                        final File xml = new File( name );
480//                                      // 7.0.2.3 (2019/04/01) xmlファイルのタイムスタンプによる更新制御を入れます。(保留)
481                                        // 7.0.6.1 (2019/10/11) xmlファイルのタイムスタンプによる更新制御を入れます。(復活)
482                                        if( xml.lastModified() > 0 ) {
483                                                // 5.1.1.0 (2009/12/01) 処理中コメント:フォルダ単位に表示
484                                                final String dir2 = xml.getParent();
485                                                if( dir1 == null || !dir1.equalsIgnoreCase( dir2 ) ) {
486                                                        out.println( "            processing ... " + dir2 );
487                                                        dir1 = dir2;
488                                                        Closer.commit( conn );                  // 5.6.7.0 (2013/07/27) Connection引数追加
489                                                }
490
491                                                // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応
492                                                try ( Reader reader = FileUtil.getBufferedReader( xml, "UTF-8" ) ) {            // 6.2.0.0 (2015/02/27) BufferedReader を2重にしていた。
493//                                                      final Reader reader = FileUtil.getBufferedReader( xml, "UTF-8" );                       // 6.2.0.0 (2015/02/27) BufferedReader を2重にしていた。
494                                                        final HybsXMLSave save = new HybsXMLSave( conn, xml.getName() );                        // 5.6.7.0 (2013/07/27) Connection引数追加
495                                                        save.onExecErrException( false );       // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
496                                                        save.insertXML( reader );
497                                                }
498                                                // 8.5.4.2 (2024/01/12) try-with-resources にしたため、暗黙的な IOException が発生する。
499                                                catch( final IOException ex ) {
500                                                        // ファイルがclose できなかった
501                                                        System.err.println( ex.getMessage() );
502                                                }
503
504                                                // 7.0.6.1 (2019/10/11) xmlファイルのタイムスタンプによる更新制御を入れます。(復活)
505//                                              xml.setLastModified( 0L );                      // 7.0.2.3 (2019/04/01) (保留) → 7.0.6.1 (2019/10/11) (復活)
506                                                if( !xml.setLastModified( 0L ) ) {      // 7.3.0.0 (2021/01/06) SpotBugs 例外的戻り値を無視しているメソッド
507                                                        out.println( "            LastModified Set Error! " + name );
508                                                }
509                                        }
510                                }
511                        }
512                        Closer.commit( conn );                  // 5.6.7.0 (2013/07/27) メソッド内でコミット処理を行う。
513                        out.println( "            DB Enviroment " + type + "ed , [ " + list.size() + " ] scripts loaded " );
514                }
515        }
516
517        /**
518         * 最後に起動された際のバージョン番号を取得します。(システムID='**')
519         *
520         * エンジンがまだインストールされていない等の原因でエラーが発生した場合は、
521         * "none"という文字列を返します。
522         *
523         * @og.rev 5.1.1.0 (2009/12/01) 実行エラー時に、rollback を追加(PostgreSQL対応)
524         * @og.rev 5.6.7.0 (2013/07/27) リソース用コネクションから取り出します。
525         * @og.rev 5.7.2.0 (2014/01/10) テーブルが無いのに正常終了するケースがある為、ver の初期値を "none" にしておきます。
526         * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。
527         *
528         * @return      バージョン番号
529         */
530        private String getOldMaxVersion() {
531                // エンジンパラメータのエンジン情報(バージョン番号 + ビルドタイプ)を取得します。
532                String                          ver             = "none";                               // 5.7.2.0 (2014/01/10) 初期値を null ⇒ "none" へ変更。
533
534                // 6.4.2.1 (2016/02/05) try-with-resources 文
535                try( Statement stmt      = rscConn.createStatement();                                   // setFetchSize は行わない。(データ件数が少ない)
536                         ResultSet resultSet = stmt.executeQuery( SEL_MAX_ENG ) ) {
537                        while( resultSet.next() ) {
538                                ver = resultSet.getString(1);
539                        }
540                }
541                catch( final SQLException ex ) {                // catch は、close() されてから呼ばれます。
542                        Closer.rollback( rscConn );                     // 5.6.7.0 (2013/07/27)
543                }
544                return ver;
545        }
546
547        /**
548         * 最後に起動された際のバージョン番号を取得します。(システムID=各システム)
549         *
550         * @og.rev 5.1.1.0 (2009/12/01) 実行エラー時に、rollback を追加(PostgreSQL対応)
551         * @og.rev 5.6.7.0 (2013/07/27) リソース用コネクションから取り出します。
552         * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。
553         *
554         * @param       systemId        システムID
555         * @param       hostUrl         ホストURL
556         *
557         * @return      バージョン番号
558         */
559        private String getOldSystemVersion( final String systemId, final String hostUrl ) {
560                // エンジンパラメータのエンジン情報(バージョン番号 + ビルドタイプ)を取得します。
561                String                          ver             = null;
562                // 6.4.2.1 (2016/02/05) try-with-resources 文
563                try( PreparedStatement pstmt = rscConn.prepareStatement( SEL_SYS_ENG ) ) {      // setFetchSize は行わない。(データ件数が少ない)
564                        pstmt.setString( 1, systemId );
565                        pstmt.setString( 2, hostUrl );
566                        try( ResultSet resultSet = pstmt.executeQuery() ) {
567                                while( resultSet.next() ) {
568                                        ver = resultSet.getString(1);
569                                }
570                        }
571                }
572                catch( final SQLException ex ) {                                // catch は、close() されてから呼ばれます。
573                        Closer.rollback( rscConn );                     // 5.6.7.0 (2013/07/27)
574                }
575                return ver;
576        }
577
578        /**
579         * エンジン内部定義の初期リソース情報をDB(GE12)に登録します。
580         *
581         * 初期リソース情報は、KBSAKU='0' で登録されている情報で、一旦すべて削除
582         * してから、全てのリソース情報を追加するという形をとります。
583         * リソースは、すでに、Oracle XDK により XMLファイル化してあります。
584         * なお、この情報をDB登録する理由は、リソースの設定値を変えたい場合に、
585         * キーが判らない(JavaDOCからしか読み取れない)のでは不便な為に
586         * 用意しておくだけで、内部では SystemData オブジェクトとして定義
587         * されている値を使用するため、このデータベース値は、使用していません。
588         *
589         * @og.rev 4.3.6.6 (2009/05/15) バージョン判定部分を分離
590         * @og.rev 5.6.7.0 (2013/07/27) リソース用コネクションに登録します。
591         * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
592         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応
593         *
594         * @throws UnsupportedEncodingException エンコード名 "UTF-8" が存在しなかった場合。
595         */
596        private void dbXMLResourceInsert() throws UnsupportedEncodingException {
597                // 新設定値を全件INSERTします。
598                // common フォルダにセットして、ClassLoader で読み取る方法
599                final ClassLoader loader = Thread.currentThread().getContextClassLoader();
600//              final InputStream stream = loader.getResourceAsStream( GE12_XML );
601                InputStream stream = null ;
602                Reader reader = null;
603
604                try {
605                        stream = loader.getResourceAsStream( GE12_XML );
606                        reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) );
607
608//              // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応
609//              try ( InputStream stream = loader.getResourceAsStream( GE12_XML );
610//                      Reader reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) ) ) {
611//                      final Reader reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) );
612                        final HybsXMLSave save = new HybsXMLSave( rscConn,"GE12" );                     // 5.6.7.0 (2013/07/27)
613                        save.onExecErrException( false );               // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
614                        save.insertXML( reader );
615                        final int insCnt = save.getInsertCount();
616                        final int delCnt = save.getDeleteCount();
617
618                        out.print( "        XML Engine Resource Reconfiguration " );
619                        out.println( "DELETE=[" + delCnt + "],INSERT=[" + insCnt + "] finished." );
620                }
621                // 8.5.4.2 (2024/01/12) try-with-resources にしたため、暗黙的な IOException が発生する。
622                catch( final IOException ex ) {
623                        // ファイルがclose できなかった
624                        System.err.println( ex.getMessage() );
625                }
626                finally {
627                        Closer.ioClose( reader );               // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 戻し
628                        Closer.ioClose( stream );               // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 戻し
629                }
630
631                // 5.6.7.0 (2013/07/27) コミットをメソッドの中で処理します。
632                Closer.commit( rscConn );
633        }
634}