001/*
002 * Copyright (c) 2017 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.fukurou.fileexec;
017
018import java.nio.file.Files;
019import java.nio.file.Path;
020import java.util.Arrays;
021import java.nio.file.PathMatcher;
022
023import static org.opengion.fukurou.fileexec.CommandLine.GE70;           // enum を簡素化して使用するための定義
024
025/**
026 * FileExec は、処理の中心で、デーモン一つに対応する処理開始クラスです。
027 *
028 *<pre>
029 * このクラスは、ファイルスキャンのフォルダ単位に、起動され、ファイルのイベントを処理します。
030 *</pre>
031 *
032 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
033 *
034 * @version  7.0
035 * @author   Kazuhiko Hasegawa
036 * @since    JDK1.8,
037 */
038public class FileExec {
039        private static final XLogger LOGGER= XLogger.getLogger( FileExec.class.getName() );             // ログ出力
040
041        /** システム依存の改行記号(String)。        */
042        public static final String CR = System.getProperty("line.separator");
043
044        private final TBL_GE71  tableGE71 ;             // 6.9.7.0 (2018/05/14) PMD
045
046        private final String    systemId ;              // システムID
047        private final String    rsrvNo;                 // 予約番号
048        private final String    execId;                 // 処理ID
049
050        private final String    fileFltr ;              // 検索条件
051
052        private final BasePath  basePath ;              // 各種パスを管理しているクラス
053
054        private final FileWatch fWatch ;                // 取込フォルダをイベントで監視する
055
056        // 7.2.1.0 (2020/03/13) 拡張子が properties と bat と jar は対象外にします。
057        private static final String PROP = ".properties" ;
058        private static final String BAT  = ".bat" ;
059        private static final String JAR  = ".jar" ;
060
061        // 7.2.1.0 (2020/03/13) 拡張子が properties , bat , jar と WORK,OK,NG は対象外にします。
062        private static final PathMatcher SCAN_EXT = path -> {
063                final String fname = path.getFileName().toString();
064                return  !( fname.endsWith( PROP ) || fname.endsWith( BAT ) || fname.endsWith( JAR ) );
065        };
066
067        /**
068         * コマンドラインを引数に取るコンストラクター
069         *
070         * ファイルの監視を開始します。
071         *
072         * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
073         * @og.rev 7.2.1.0 (2020/03/13) WORK_PATH,OK_PATH,NG_PATH のスキャンは行わない
074         *
075         * @param       cmndLine        コマンドラインオブジェクト
076         */
077        public FileExec( final CommandLine cmndLine ) {
078                LOGGER.debug( () -> "② CommandLine=" + cmndLine );
079
080                systemId        = cmndLine.getValue( GE70.SYSTEM_ID );          // システムID
081                rsrvNo          = cmndLine.getValue( GE70.RSRV_NO );            // 予約番号
082                execId          = cmndLine.getValue( GE70.EXECID );                     // 処理ID
083                fileFltr        = cmndLine.getValue( GE70.FILE_FILTER );        // 検索条件
084
085                basePath = new BasePath(
086                                        cmndLine.getValue( GE70.DIR_BASE )              ,       // 取込ベースフォルダ
087                                        cmndLine.getValue( GE70.DIR_SUB  )              ,       // 取込サブフォルダ
088                                        cmndLine.getValue( GE70.DIR_WORK    )   ,       // 処理フォルダ(WORK)
089                                        cmndLine.getValue( GE70.DIR_BKUP_OK )   ,       // 処理済フォルダ(正常)
090                                        cmndLine.getValue( GE70.DIR_BKUP_NG ) );        // 処理済フォルダ(異常)
091
092                tableGE71 = new TBL_GE71( systemId,rsrvNo,execId );             // 6.9.7.0 (2018/05/14) PMD
093
094                fWatch  = new FileWatch( basePath.SUB_PATH );                   // サブフォルダをイベントで監視する
095                fWatch.setPathMatcher( SCAN_EXT );                                                              // 7.2.1.0 (2020/03/13)
096                fWatch.setPathMatcher( path -> basePath.isScanPath( path ) );   // 7.2.1.0 (2020/03/13)
097        }
098
099        /**
100         * このコマンドに対応するフォルダの監視を開始します。
101         *
102         * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
103         */
104        public void watchStart() {
105                LOGGER.debug( () -> "④ [watchStart()]" );
106
107                fWatch.setEventKinds( FileWatch.CREATE,FileWatch.MODIFY );
108                fWatch.setPathMatcher( new PathMatcherSet().addFileName( fileFltr ) );          // ファイルの検索条件
109                fWatch.callback( (event,fPath) -> checkFile( event,fPath ) );
110                fWatch.start();
111        }
112
113        /**
114         * このコマンドに対応するフォルダの監視を終了します。
115         *
116         * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
117         */
118        public void watchStop() {
119                LOGGER.debug( () -> "⑩ [watchStop()]" );
120
121                AppliExec.removeInstance( systemId,rsrvNo );            // 7.2.1.0 (2020/03/13)
122
123                fWatch.stop();
124        }
125
126        /**
127         * 更新されたファイルをチェックします。
128         *
129         * ※ バックアップ処理してから、DB取り込み処理を行います。
130         * よって、DB登録処理中にエラーが発生した場合でも、バックアップ済みです
131         *
132         * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
133         * @og.rev 7.2.1.0 (2020/03/13) ディレクトリか存在しない場合は、処理を実行しません。
134         *
135         * @param       event     発生イベントの名称
136         * @param       filePath ファイルパス(相対パス)
137         */
138        private void checkFile( final String event,final Path filePath ) {
139                if( Files.isDirectory( filePath ) || !Files.exists( filePath ) ) { return; }    // 7.2.1.0 (2020/03/13)
140
141                String  fgtKan  = "0" ;                 // 取込完了フラグ 0:取込なし 1:処理中 2:済 7:デーモンエラー 8:アプリエラー
142                Path    bkup    = null;
143                String  errMsg  = "";
144                int             suKekka = -1;
145
146                final String  tmStr = StringUtil.getTimeFormat();                                                               // 開始時刻
147                AppliExec appli = null;
148                try {
149                        // FileUtil.stablePath は、書き込まれている途中かもしれないので、安定するまで待つ。
150                        if( FileUtil.stablePath( filePath ) ) {
151                                LOGGER.debug( () -> "⑤ event=" + event + " , Path=" + filePath );
152
153                                appli = AppliExec.newInstance( systemId,rsrvNo,execId );                // 7.2.1.0 (2020/03/13)
154                                // ワークへ移動してから、DB取り込み処理を行います。
155                                bkup = FileUtil.backup( filePath,basePath.WORK_PATH );                  // WORKに移動します。
156                                suKekka = appli.exec( bkup );
157
158                                if( suKekka >= 0 ) {
159//                                      okFile = FileUtil.backup( bkup,basePath.OK_PATH );                      // 処理済OKフォルダに移動
160                                        fgtKan = "2" ;          // 2:済
161                                }
162                                else {
163//                                      ngFile = FileUtil.backup( bkup,basePath.NG_PATH );                      // 処理済NGフォルダに移動
164                                        fgtKan = "8" ;          // 8:アプリエラー
165                                }
166
167//                              tableGE71.dbInsert( fgtKan,tmStr,filePath,okFile,ngFile,suKekka,errMsg );
168
169        //                      LOGGER.info( () -> "DAT execute. " + filePath + " , FGTKAN=" + fgtKan + " , kensu=" + suKekka );
170                        }
171                        else {
172                                // エラーにせず、ワークへ移動もせず、保留にします。
173                                LOGGER.info( () -> "checkFile Not stablePath. " + filePath );
174                                return ;
175                        }
176                }
177                catch( final Throwable th ) {
178                        fgtKan = "7" ;                          // 7:デーモンエラー
179
180                        // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
181                        errMsg = MsgUtil.errPrintln( th,"MSG0021",filePath );
182
183                        // 6.9.8.0 (2018/05/28) FindBugs: メソッド呼び出しは非 null パラメータに対して null を渡している
184//                      if( Files.exists( bkup ) ) {                                                            // WORKに移動するところまでは、成功しており、その後の、移動ができていない。
185//                      if( bkup != null && Files.exists( bkup ) ) {                            // WORKに移動するところまでは、成功しており、その後の、移動ができていない。
186//                              ngFile = FileUtil.backup( bkup,basePath.NG_PATH );              // 処理済NGフォルダに移動
187//                      }
188
189//                      tableGE71.dbInsert( fgtKan,tmStr,filePath,okFile,ngFile,suKekka,errMsg );
190                }
191
192                // checkFile Not stablePath. の時は、appli == null
193                if( appli != null ) {
194                        try {
195                                // appli.exec が正常でもエラーでも実行する。
196                                appli.endExec( bkup,fgtKan,errMsg );
197                        }
198                        catch( final Throwable th ) {
199                                if( fgtKan != "7" ) {                                   // 7:デーモンエラーは、上書きしない。
200                                        fgtKan = "8" ;
201                                }
202                                // MSG0029 = PL/SQlの実行時にエラーが発生しました。\n\t[{0}]
203                                errMsg = MsgUtil.errPrintln( th,"MSG0029",filePath );
204                        }
205                }
206
207                // 6.9.8.0 (2018/05/28) FindBugs: メソッド呼び出しは非 null パラメータに対して null を渡している
208                Path    okFile  = null;
209                Path    ngFile  = null;
210                if( bkup != null && Files.exists( bkup ) ) {                            // WORKに移動するところまでは、成功しており、その後の、移動ができていない。
211                        if( "2".equals( fgtKan ) ) {
212                                okFile = FileUtil.backup( bkup,basePath.OK_PATH );              // 処理済OKフォルダに移動
213                        }
214                        else {
215                                ngFile = FileUtil.backup( bkup,basePath.NG_PATH );              // 処理済NGフォルダに移動
216                        }
217                }
218
219                tableGE71.dbInsert( fgtKan,tmStr,filePath,okFile,ngFile,suKekka,errMsg );
220        }
221
222        /**
223         *このクラスの文字列表現を返します。
224         *
225         * @return      クラスの文字列表現
226         */
227        @Override
228        public String toString() {
229//              return systemId + " , " + execId ;
230                return String.join( ",",systemId,rsrvNo,execId );               // 6.9.7.0 (2018/05/14) PMD
231        }
232
233        /**
234         * GE71 実行結果をデータベースに書き込む内部クラスです。 ( 6.9.7.0 (2018/05/14) PMD )
235         */
236        private static final class TBL_GE71 {
237                private static final String[] KEYS              = new String[] {  "SYSTEM_ID","RSRV_NO","EXECID","FGTKAN","TMSTR","TMEND"
238                                                                                                                                , "FILE_IN","FILE_OK","FILE_NG","SUTORI ","ERRMSG "
239                                                                                                                                , "DYSET","DYUPD" };
240                private static final String[] CON_KEYS  = new String[] { "FGJ","PGSET"  ,"PGUPD"  };
241                private static final String[] CON_VALS  = new String[] { "1"  ,"FileExec","FileExec" };
242
243                private static final String INS_QUERY = DBUtil.getInsertSQL( "GE71",KEYS,CON_KEYS,CON_VALS );
244
245                private final String systemId ;                 // システムID
246                private final String rsrvNo;                    // 予約番号
247                private final String execId ;                   // 処理ID
248
249                /**
250                 * GE71 データベースにインサート処理を行うクラスのコンストラクター
251                 *
252                 * @param       sysId   システムID
253                 * @param       rsNo    予約番号
254                 * @param       exId    処理ID
255                 */
256                public TBL_GE71( final String sysId,final String rsNo ,final String exId ) {
257                        systemId = sysId ;
258                        rsrvNo   = rsNo ;
259                        execId   = exId;
260                }
261
262                /**
263                 * データベースにインサート処理を行います。
264                 *
265                 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
266                 *
267                 * @param       fgtKan  取込完了フラグ
268                 * @param       tmStr   開始時刻
269                 * @param       fIn             取込ファイルパス
270                 * @param       fOk             処理済OKファイルパス
271                 * @param       fNg             処理済NGファイルパス
272                 * @param       sutori  取込数
273                 * @param       errMsg  エラーメッセージ
274                 */
275                public void dbInsert( final String fgtKan,final String tmStr,final Path fIn,final Path fOk,final Path fNg,final int sutori,final String errMsg ) {
276                        final String NOW = StringUtil.getTimeFormat();
277
278                        // ファイルは、ファイル名のみとします。
279                        final String fileIn = fIn == null ? "" : fIn.getFileName().toString() ;
280                        final String fileOk = fOk == null ? "" : fOk.getFileName().toString() ;
281                        final String fileNg = fNg == null ? "" : fNg.getFileName().toString() ;
282
283                        // GE71 テーブルのカラム
284                        final String[] values = new String[] {
285                                  systemId                                      // システムID               SYSTEM_ID
286                                , rsrvNo                                        // 予約番号         RSRV_NO
287                                , execId                                        // 処理ID         EXECID
288                                , fgtKan                                        // 取込完了フラグ     FGTKAN
289                                , tmStr                                         // 開始時刻         TMSTR
290                                , NOW                                           // 完了時刻         TMEND
291                                , fileIn                                        // 取込ファイル               FILE_IN
292                                , fileOk                                        // 処理済OKファイル    FILE_OK
293                                , fileNg                                        // 処理済NGファイル    FILE_NG
294                                , String.valueOf( sutori )      // 取込件数         SUTORI
295                                , errMsg                                        // エラーメッセージ    ERRMSG
296                                , NOW                                           // 登録日時         DYSET
297                                , NOW                                           // 更新日時         DYUPD
298                        } ;
299
300                        LOGGER.debug( () -> "⑥ GE71.dbInsert query=" + INS_QUERY + "\n\t values=" + Arrays.toString( values ) );
301
302                        DBUtil.execute( INS_QUERY,values );
303                }
304        }
305}