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
018// import java.io.File;
019import java.io.IOException;
020
021import java.nio.file.WatchEvent;
022import java.nio.file.Path;
023import java.nio.file.PathMatcher;
024import java.nio.file.FileSystem;
025import java.nio.file.FileSystems;                                                       // 7.4.4.0 (2021/06/30)
026import java.nio.file.WatchKey;
027import java.nio.file.StandardWatchEventKinds;
028import java.nio.file.WatchService;
029
030import java.util.function.BiConsumer;
031// import java.util.concurrent.atomic.AtomicBoolean;            // 7.2.9.4 (2020/11/20) volatile boolean の代替え , // 7.4.4.0 (2021/06/30) 戻す
032
033/**
034 * FileWatch は、ファイル監視を行うクラスです。
035 *
036 *<pre>
037 * ファイルが、追加(作成)、変更、削除された場合に、イベントが発生します。
038 * このクラスは、Runnable インターフェースを実装しているため、Thread で実行することで、
039 * 個々のフォルダの監視を行います。
040 *
041 *</pre>
042 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
043 *
044 * @version  7.0
045 * @author   Kazuhiko Hasegawa
046 * @since    JDK1.8,
047 */
048public class FileWatch implements Runnable {
049        private static final XLogger LOGGER= XLogger.getLogger( FileWatch.class.getSimpleName() );              // ログ出力
050
051        /** Path に、WatchService を register するときの作成イベントの簡易指定できるように。 */
052        public static final WatchEvent.Kind<Path> CREATE = StandardWatchEventKinds.ENTRY_CREATE ;
053
054        /** Path に、WatchService を register するときの変更イベントの簡易指定できるように。 */
055        public static final WatchEvent.Kind<Path> MODIFY = StandardWatchEventKinds.ENTRY_MODIFY ;
056
057        /** Path に、WatchService を register するときの削除イベントの簡易指定できるように。 */
058        public static final WatchEvent.Kind<Path> DELETE = StandardWatchEventKinds.ENTRY_DELETE ;
059
060        /** Path に、WatchService を register するときの特定不能時イベントの簡易指定できるように。 */
061        public static final WatchEvent.Kind<?>    OVERFLOW = StandardWatchEventKinds.OVERFLOW ;
062
063        /** Path に、WatchService を register するときのイベント */
064        // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer
065//      private static final WatchEvent.Kind<?>[] WE_KIND = new WatchEvent.Kind<?>[] { CREATE , MODIFY , DELETE , OVERFLOW };
066        private static final WatchEvent.Kind<?>[] WE_KIND = { CREATE , MODIFY , DELETE , OVERFLOW };
067
068        /** Path に、WatchService を register するときの登録方法の修飾子(修飾子 なしの場合) */
069        private static final WatchEvent.Modifier[] WE_MOD_ONE  = new WatchEvent.Modifier[0];    // Modifier なし
070
071        /** Path に、WatchService を register するときの登録方法の修飾子(以下の階層も監視対象にします) */
072        // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer
073//      private static final WatchEvent.Modifier[] WE_MOD_TREE = new WatchEvent.Modifier[] {    // ツリー階層
074        private static final WatchEvent.Modifier[] WE_MOD_TREE = {                      // ツリー階層
075                // 8.5.3.3 (2023/10/27) 警告: ExtendedWatchEventModifierは内部所有のAPIであり、今後のリリースで削除される可能性があります
076//              com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE
077                () -> "FILE_TREE"                       // interface java.nio.file.WatchEvent.Modifier をラムダ式で表現
078        };
079
080        /** DirWatch でスキャンした場合のイベント名 {@value} */
081        public static final String DIR_WATCH_EVENT = "DirWatch";
082
083        /** 7.4.4.0 (2021/06/30) stop() してから、実際に止まるまで 待機する時間 (ms ) */
084        public static final int STOP_WATI_TIME = 500 ;
085
086        /** 7.4.4.0 (2021/06/30) stop() してから、実際に止まるまで 待機する回数 */
087        public static final int STOP_WATI_CNT = 5 ;
088
089        /** 監視対象のフォルダ */
090        private final Path dirPath ;
091
092        /** 監視方法 */
093        private final boolean   useTree ;
094        private final WatchEvent.Modifier[] extModifiers ;
095
096        /** callbackするための、関数型インターフェース(メソッド参照) */
097        private BiConsumer<String,Path> action = (event,path) -> System.out.println( "Event=" + event + " , Path=" + path ) ;
098
099        /** Path に、WatchService を register するときのイベント */
100        private WatchEvent.Kind<?>[] weKind = WE_KIND ;                                         // 初期値は、すべて
101
102        /** パスの照合操作を行うPathMatcher の初期値 */
103        private final PathMatcherSet pathMchSet = new PathMatcherSet();         // PathMatcher インターフェースを継承
104
105        /** DirWatchのパスの照合操作を行うPathMatcher の初期値 */
106        private final PathMatcherSet dirWatchMch = new PathMatcherSet();        // PathMatcher インターフェースを継承
107
108        /** 何らかの原因でイベントもれした場合、フォルダスキャンを行います。 */
109        private boolean         useDirWatch     = true;                                                         // 初期値は、イベント漏れ監視を行います。
110        private DirWatch        dWatch ;                                                                                // DirWatch のstop時に呼び出すための変数
111        private Thread          thread ;                                                                                // 停止するときに呼び出すため
112
113        /** 状態とThreadの停止に使用する。 */
114        private boolean running ;                                                                                       // 8.5.4.2 (2024/01/12) 無しでもいいか…
115//      private volatile boolean running ;                                                                      // 状態とThreadの停止に使用する。 // 7.4.4.0 (2021/06/30) 復活
116//      private final AtomicBoolean running = new AtomicBoolean();                      // 7.2.9.4 (2020/11/20) volatile boolean の代替え ( 状態とThreadの停止に使用する。)
117
118        /**
119         * 処理対象のフォルダのパスオブジェクトを指定して、ファイル監視インスタンスを作成します。
120         *
121         * ここでは、指定のフォルダの内のファイルのみ監視します。
122         * これは、new FileWatch( dir , false ) とまったく同じです。
123         *
124         * @param dir   処理対象のフォルダオブジェクト
125         */
126        public FileWatch( final Path dir ) {
127                this( dir , false );
128        }
129
130        /**
131         * 処理対象のフォルダのパスオブジェクトと、監視対象方法を指定して、ファイル監視インスタンスを作成します。
132         *
133         * useTree を true に設定すると、指定のフォルダの内のフォルダ階層を、すべて監視対象とします。
134         *
135         * @param dir   処理対象のフォルダのパスオブジェクト
136         * @param useTree       フォルダツリーの階層をさかのぼって監視するかどうか(true:フォルダ階層を下る)
137         */
138        public FileWatch( final Path dir , final boolean useTree ) {
139                dirPath          = dir ;
140                this.useTree = useTree;
141                extModifiers = useTree ? WE_MOD_TREE : WE_MOD_ONE ;
142        }
143
144        /**
145         * 指定のイベントの種類のみ、監視対象に設定します。
146         *
147         * ここで指定したイベントのみ、監視対象になり、callback されます。
148         * 第一引数は、イベントの種類(ENTRY_CREATE,ENTRY_MODIFY,ENTRY_DELETE,OVERFLOW)
149         *
150         * @param       kind 監視対象に設定するイベントの種類
151         * @see         java.nio.file.StandardWatchEventKinds
152         */
153        public void setEventKinds( final WatchEvent.Kind<?>... kind ) {
154                if( kind != null && kind.length > 0 ) {
155                        weKind = kind;
156                }
157        }
158
159        /**
160         * 指定のパスの照合操作で、パターンに一致したパスのみ、callback されます。
161         *
162         * ここで指定したパターンの一致を判定し、一致した場合は、callback されます。
163         * 指定しない場合は、すべて許可されたことになります。
164         * なお、#setPathEndsWith(String...) と、この設定は同時には行うことは出来ません。
165         * (最後に登録した条件が、適用されます。)
166         *
167         * @param       pathMch パスの照合操作のパターン
168         * @see         java.nio.file.PathMatcher
169         * @see         #setPathEndsWith(String...)
170         */
171        public void setPathMatcher( final PathMatcher pathMch ) {
172                pathMchSet.addPathMatcher( pathMch );
173        }
174
175        /**
176         * 指定のパスが、指定の文字列と、終端一致(endsWith) したパスのみ、callback されます。
177         *
178         * これは、#setPathMatcher(PathMatcher) の簡易指定版です。
179         * 指定の終端文字列(一般には拡張子)のうち、ひとつでも一致すれば、true となりcallback されます。
180         * 指定しない場合(null)は、すべて許可されたことになります。
181         * 終端文字列の判定には、大文字小文字の区別を行いません。
182         * なお、#setPathMatcher(PathMatcher) と、この設定は同時には行うことは出来ません。
183         * (最後に登録した条件が、適用されます。)
184         *
185         * @param       endKey パスの終端一致のパターン
186         * @see         #setPathMatcher(PathMatcher)
187         */
188        public void setPathEndsWith( final String... endKey ) {
189                pathMchSet.addEndsWith( endKey );
190        }
191
192        /**
193         * イベントの種類と、ファイルパスを、引数に取る BiConsumer ダオブジェクトを設定します。
194         *
195         * これは、関数型インタフェースなので、ラムダ式またはメソッド参照の代入先として使用できます。
196         * イベントが発生したときの イベントの種類と、そのファイルパスを引数に、accept(String,Path) メソッドが呼ばれます。
197         * 第一引数は、イベントの種類(ENTRY_CREATE,ENTRY_MODIFY,ENTRY_DELETE,OVERFLOW)
198         * 第二引数は、ファイルパス(監視フォルダで、resolveされた、正式なフルパス)
199         *
200         * @param       act 2つの入力(イベントの種類 とファイルパス) を受け取る関数型インタフェース
201         * @see         BiConsumer#accept(Object,Object)
202         */
203        public void callback( final BiConsumer<String,Path> act ) {
204                if( act != null ) {
205                        action = act ;
206                }
207        }
208
209        /**
210         * 何らかの原因でイベントを掴み損ねた場合に、フォルダスキャンするかどうかを指定します。
211         *
212         * スキャン開始の遅延時間と、スキャン間隔、ファイルのタイムスタンプとの比較時間等は、
213         * DirWatch の初期値をそのまま使用するため、ここでは指定できません。
214         * 個別に指定したい場合は、このフラグをfalse にセットして、個別に、DirWatch を作成してください。
215         * このメソッドでは、#setPathEndsWith( String... )や、#setPathMatcher( PathMatcher ) で
216         * 指定した条件が、そのまま適用されます。
217         *
218         * @param       flag フォルダスキャンするかどうか(true:する/false:しない)
219         * @see         DirWatch
220         */
221        public void setUseDirWatch( final boolean flag ) {
222                useDirWatch = flag;
223        }
224
225        /**
226         * 何らかの原因でイベントを掴み損ねた場合の、フォルダスキャンの対象ファイルの拡張子を指定します。
227         *
228         * このメソッドを使用する場合は、useDirWatch は、true にセットされます。
229         * スキャン開始の遅延時間と、スキャン間隔、ファイルのタイムスタンプとの比較時間等は、
230         * DirWatch の初期値をそのまま使用するため、ここでは指定できません。
231         * このメソッドでは、DirWatch 対象の終端パターンを独自に指定できますが、FileWatch で
232         * で指定した条件も、クリアされるので、含める必要があります。
233         *
234         * @param       endKey パスの終端一致のパターン
235         * @see         DirWatch
236         */
237        public void setDirWatchEndsWith( final String... endKey ) {
238                if( endKey != null && endKey.length > 0 ) {
239                        useDirWatch = true;                                     // 対象があれば、実行するが、true になる。
240
241                        dirWatchMch.addEndsWith( endKey );
242                }
243        }
244
245        /**
246         * このファイル監視で、最後に処理した結果が、エラーの場合に、true を返します。
247         *
248         * 通常は、対象フォルダが見つからない場合や、フォルダスキャン(DirWatch)で
249         * エラーが発生した場合に、true にセットされます。
250         * また、stop() メソッドが呼ばれた場合も、true にセットされます。
251         *
252         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
253         *
254         * @return      エラー状態(true:エラー,false:正常)
255         */
256        public boolean isErrorStatus() {
257                // DirWatch を使用している場合は、その結果も加味します。
258//              return isError || dWatch != null && dWatch.isErrorStatus() ;
259//              return !running.get() || dWatch != null && dWatch.isErrorStatus() ;             // 7.2.9.4 (2020/11/20)
260                return !running || dWatch != null && dWatch.isErrorStatus() ;                   // 7.4.4.0 (2021/06/30) 復活
261        }
262
263        /**
264         * フォルダの監視を開始します。
265         *
266         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
267         * @og.rev 7.4.4.0 (2021/06/30) stop() してから、実際に止まるまでの時間、待機します。
268         * @og.rev 8.1.0.3 (2022/01/21) スレッドに名前を付けておきます。
269         *
270         * 自身を、Threadに登録して、Thread#start() を実行します。
271         * 内部の Thread オブジェクトがなければ、新しく作成します。
272         * すでに、実行中の場合は、何もしません。
273         * 条件を変えて、実行したい場合は、stop() メソッドで、一旦スレッドを
274         * 停止させてから、再び、#start() メソッドを呼び出してください。
275         */
276        public void start() {
277                // 7.4.4.0 (2021/06/30) stop() してから、実際に止まるまでの時間、待機します。
278                int cnt = 0;
279                while( running ) {
280                        cnt++ ;
281//                      try{ Thread.sleep( STOP_WATI_TIME ); } catch( final InterruptedException ex ){}
282                        try { Thread.sleep( STOP_WATI_TIME ); } catch( final InterruptedException ignored ) {}  // 8.5.4.2 (2024/01/12) PMD 7.0.0 EmptyCatchBlock
283                        if( cnt >= STOP_WATI_CNT ) {    // ループ後も、まだ、stop() 出来ていない場合。
284                                LOGGER.warning( () -> "FileWatch Stop Error : [" + dirPath + "]" );
285                        }
286                }
287
288                running = true;                         // 7.4.4.0 (2021/06/30) 復活
289
290                if( thread == null ) {
291//                      thread = new Thread( this );
292                        thread = new Thread( this,"FileWatch" );                // 8.1.0.3 (2022/01/21)
293//                      running = true;
294//                      running.set( true );    // 7.2.9.4 (2020/11/20)
295                        thread.start();                 // running=true; を先に行わないと、すぐに終了してしまう。
296                }
297
298                // 監視漏れのファイルを、一定時間でスキャンする
299                if( useDirWatch ) {
300                        dWatch = new DirWatch( dirPath,useTree );
301                        if( dirWatchMch.isEmpty() ) {                   // 初期値は、未登録時は、本体と同じPathMatcher を使用します。
302                                dWatch.setPathMatcher( pathMchSet );
303                        }
304                        else {
305                                dWatch.setPathMatcher( dirWatchMch );
306                        }
307                        dWatch.callback( path -> action.accept( DIR_WATCH_EVENT , path ) ) ;    // BiConsumer<String,Path> を Consumer<Path> に変換しています。
308                        dWatch.start();
309                }
310        }
311
312        /**
313         * フォルダの監視を終了します。
314         *
315         * 自身を登録しているThreadに、割り込みをかけるため、
316         * Thread#interrupt() を実行します。
317         * フォルダ監視は、ファイル変更イベントが発生するまで待機していますが、
318         * interrupt() を実行すると、強制的に中断できます。
319         * 内部の Thread オブジェクトは、破棄するため、再び、start() メソッドで
320         * 実行再開することが可能です。
321         *
322         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
323         * @og.rev 7.4.4.0 (2021/06/30) thread の存在有無にかかわらず、running は停止状態にする。
324         */
325        public void stop() {
326                // 7.4.4.0 (2021/06/30) thread の存在有無にかかわらず、running は停止状態にする。
327                running = false;                // 7.4.4.0 (2021/06/30) 復活
328
329                if( thread != null ) {
330//                      running = false;
331        //              running.set( false );           // 7.2.9.4 (2020/11/20)
332                        thread.interrupt();
333        //              thread = null;                  1.1.0 (2018/02/01) stop() 時に null を入れると、interrupt() 後の処理が継続できなくなる。
334        //              なので、run()の最後に、thread = null を入れておきます。
335                }
336
337                if( dWatch != null ) {
338                        dWatch.stop();
339                        dWatch = null;
340                }
341        }
342
343        /**
344         * Runnableインターフェースのrunメソッドです。
345         *
346         * 規定のスケジュール時刻が来ると、呼ばれる runメソッドです。
347         *
348         * @og.rev 7.2.5.0 (2020/06/01) LOGGERを使用します。
349         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
350         */
351        @Override       // Runnable
352        public void run() {
353                try {
354                        execute();
355                }
356                catch( final IOException ex ) {
357                        // MSG0102 = ファイル監視に失敗しました。 Path=[{0}]
358//                      MsgUtil.errPrintln( ex , "MSG0102" , dirPath );
359                        final String errMsg = "FileWatch#run : Path=" + dirPath ;
360                        LOGGER.warning( ex , "MSG0102" , errMsg );
361                }
362                catch( final Throwable th ) {
363                        // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
364//                      MsgUtil.errPrintln( th , "MSG0021" , toString() );
365                        final String errMsg = "FileWatch#run : Path=" + dirPath ;
366                        LOGGER.warning( th , "MSG0021" , errMsg );
367                }
368                finally {
369                        running = false;                        // 7.4.4.0 (2021/06/30) 停止条件だが、予期せぬエラーで停止した場合も、設定する。
370                        thread  = null;                         // 7.2.5.0 (2020/06/01) 停止処理
371//                      running = false;
372//                      running.set( false );           // 7.2.9.4 (2020/11/20)
373                }
374        }
375
376        /**
377         * runメソッドから呼ばれる、実際の処理。
378         *
379         * try ・・・ catch( Throwable ) 構文を、runメソッドの標準的な作りにしておきたいため、
380         * あえて、実行メソッドを分けているだけです。
381         *
382         * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
383         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え
384         * @og.rev 8.0.0.0 (2021/07/01) dirPathのsynchronized作成
385         *
386         * @throws IOException 入出力実行でエラーが出た場合
387         */
388        private void execute() throws IOException {
389                // ファイル監視などの機能は新しいNIO2クラスで拡張されたので
390                // 旧File型から、新しいPath型に変換する.
391                LOGGER.info( () -> "FileWatch Start: " + dirPath );
392
393                // デフォルトのファイル・システムを閉じることはできません。(UnsupportedOperationException がスローされる)
394                // なので、try-with-resources 文 (AutoCloseable) に、入れません。
395//              final FileSystem fs = dirPath.getFileSystem();                  // フォルダが属するファイルシステムを得る()
396                final FileSystem fs = FileSystems.getDefault();                 // 7.4.4.0 (2021/06/30) 上記と同じオブジェクトだから。
397
398                // try-with-resources 文 (AutoCloseable)
399                // ファイルシステムに対応する監視サービスを構築する.
400                // (一つのサービスで複数の監視が可能)
401                try( WatchService watcher = fs.newWatchService() ) {
402                        // フォルダに対して監視サービスを登録する.
403                        final WatchKey watchKey = dirPath.register( watcher , weKind , extModifiers );
404
405                        // 監視が有効であるかぎり、ループする.
406                        // (監視がcancelされるか、監視サービスが停止した場合はfalseとなる)
407                        try {
408                                boolean flag = true;
409                                while( flag && running ) {                                                                              // 7.4.4.0 (2021/06/30) 復活
410//                              while( flag && running.get() ) {                                                                // 7.2.9.4 (2020/11/20)
411                                        // スレッドの割り込み = 終了要求を判定する.
412                        //              if( Thread.currentThread().isInterrupted() ) {
413                        //                      throw new InterruptedException();
414                        //              }
415
416                                        // take は、ファイル変更イベントが発生するまで待機する.
417                                        final WatchKey detectKey = watcher.take();                      // poll() は、キューが空の場合はブロックせずに null を返す
418
419                                        // イベント発生元を判定する
420//                                      if( detectKey.equals( watchKey ) ) {
421                                        if( watchKey.equals( detectKey ) ) {                            // 8.0.0.0 (2021/07/01) 入れ替え(null対応)
422                                                // 発生したイベント内容をプリントする.
423                                                for( final WatchEvent<?> event : detectKey.pollEvents() ) {
424                                                        // 追加・変更・削除対象のファイルを取得する.
425                                                        // (ただし、overflow時などはnullとなることに注意)
426                                                        final Path path = (Path)event.context();
427                                                        if( path != null && pathMchSet.matches( path ) ) {
428                                                                final Path fpath = dirPath.resolve( path );
429                                                                synchronized( dirPath ) {                               // 8.0.0.0 (2021/07/01) dirPathのsynchronized作成
430                                                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 LinguisticNaming
431//                                                                      if( dWatch == null || dWatch.setAdd( fpath) ) {         // このセット内に、指定された要素がなかった場合はtrue
432                                                                        if( dWatch == null || dWatch.pathSetAdd( fpath) ) {     // このセット内に、指定された要素がなかった場合はtrue
433                                                                                action.accept( event.kind().name() , fpath );
434                                                                        }
435                                                                        else {
436                                                                                // CREATE と MODIFY などのイベントが連続して発生するケースへの対応
437                                                                                LOGGER.info( () -> "WatchEvent Duplication: " + fpath );
438                                                                        }
439                                                                }
440                                                        }
441                                                }
442                                        }
443
444                                        // イベントの受付を再開する.
445                                        if( detectKey != null ) {               // 8.0.0.0 (2021/07/01) null対応
446                                                detectKey.reset();
447                                        }
448
449                                        if( dWatch != null ) {
450                                                dWatch.setClear();                      // Path重複チェック用のSetは、一連のイベント完了時にクリアしておきます。
451                                        }
452
453                                        // 監視サービスが活きている、または、スレッドの割り込み( = 終了要求)がないことを、をチェックする。
454                                        flag = watchKey.isValid() && !Thread.currentThread().isInterrupted() ;
455
456                                        // 7.4.4.0 (2021/06/30) ※ 63フォルダ以上は、監視できない?(Tomcat上では?)
457                                        if( !watchKey.isValid() ) {
458                                                LOGGER.warning( () -> "FileWatch No isValid : [" + dirPath + "]" );
459                                        }
460                                }
461                        }
462                        catch( final InterruptedException ex ) {
463//                              LOGGER.warning( () -> "【WARNING】 FileWatch Canceled:" + dirPath );
464                                LOGGER.warning( () -> "FileWatch Canceled : [" + dirPath + "]" );
465                        }
466                        finally {
467                                // スレッドの割り込み = 終了要求なので監視をキャンセルしループを終了する。
468                                if( watchKey != null ) {
469                                        watchKey.cancel();
470                                }
471                        }
472                }
473                // FileSystemの実装(sun.nio.fs.WindowsFileSystem)は、close() 未サポート
474                catch( final UnsupportedOperationException ex ) {
475                        LOGGER.warning( () -> "FileSystem close : [" + dirPath + "]" );
476                }
477
478                // 7.4.4.0 (2021/06/30) 念のため、入れておきます。
479                catch( final Throwable th ) {
480                        // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
481                        final String errMsg = "FileWatch#execute : Path=" + dirPath ;
482                        LOGGER.warning( th , "MSG0021" , errMsg );
483                }
484
485//              LOGGER.info( () -> "FileWatch End: " + dirPath );
486                LOGGER.info( () -> "FileWatch End : [" + dirPath + "]" );
487
488//              thread  = null;                                 // 1.1.0 (2018/02/01) 停止処理
489        //      isError = true;                                 // 何らかの原因で停止すれば、エラーと判断します。
490        }
491
492        /**
493         *このオブジェクトの文字列表現を返します。
494         *
495         * @return      このオブジェクトの文字列表現
496         */
497        @Override               // Object
498        public String toString() {
499                return getClass().getSimpleName() + ":" + dirPath + " , " + DIR_WATCH_EVENT + "=[" + useDirWatch + "]" ;
500        }
501}