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.util.Arrays;
019import java.util.List;
020import java.util.Set;                                                                           // 7.2.5.0 (2020/06/01)
021// import java.util.HashSet;                                                            // 7.2.5.0 (2020/06/01)
022import java.util.Collections;                                                           // 7.2.9.4 (2020/11/20)
023import java.util.concurrent.ConcurrentMap;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.ScheduledFuture;                            // 7.2.5.0 (2020/06/01)
026import java.util.concurrent.ScheduledExecutorService;           // 7.2.5.0 (2020/06/01)
027import java.util.concurrent.Executors;                                          // 7.2.5.0 (2020/06/01)
028import java.util.concurrent.TimeUnit;                                           // 7.2.5.0 (2020/06/01)
029import java.util.concurrent.atomic.AtomicBoolean;                       // 7.2.9.4 (2020/11/20) volatile boolean の代替え
030
031import org.opengion.fukurou.system.HybsConst;                           // 7.2.5.0 (2020/06/01)
032import static org.opengion.fukurou.fileexec.CommandLine.GE70;           // enum を簡素化して使用するための定義
033
034/**
035 * MainProcess は、単独で使用する ファイル取込システムのメインクラスです。
036 *
037 *<pre>
038 * このクラスのmainメソッドから起動します。
039 * コマンドラインを処理することで、各種処理を実行します。
040 *
041 *</pre>
042 *
043 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
044 * @og.rev 7.2.5.0 (2020/06/01) TomcatのServletContextListenerから実行できるように修正
045 *
046 * @version  7.0
047 * @author   Kazuhiko Hasegawa
048 * @since    JDK1.8,
049 */
050// public final class MainProcess {
051public final class MainProcess implements Runnable {
052        private static final XLogger LOGGER= XLogger.getLogger( MainProcess.class.getSimpleName() );    // ログ出力
053
054        /** 7.2.5.0 (2020/06/01) エラーの場合、リロードするが、その待機時間 {@value}(秒) */
055        public static final long WAIT_TIME = 30 * 1000L;
056
057//      private static final ScheduledExecutorService   scheduler = Executors.newScheduledThreadPool(5);        // 1.5.0 (2020/04/01)
058        private static final ScheduledExecutorService   SCHEDULER = Executors.newScheduledThreadPool(5);        // 7.2.9.4 (2020/11/20) 1.5.0 (2020/04/01)
059//      private static final Set<ScheduledFuture<?>>    futureSet = new HashSet<>();
060        // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseDiamondOperator 対応
061//      private static final Set<ScheduledFuture<?>>    FUTURE_SET = Collections.newSetFromMap( new ConcurrentHashMap<ScheduledFuture<?>, Boolean>() ); // 7.2.9.4 (2020/11/20)
062        private static final Set<ScheduledFuture<?>>    FUTURE_SET = Collections.newSetFromMap( new ConcurrentHashMap<>() );    // 7.2.9.4 (2020/11/20)
063
064        /** 7.2.5.0 (2020/06/01) 開始しているかどうかを確認するための状態変数 */
065//      private static volatile boolean isStart ;
066        private static final AtomicBoolean IS_START = new AtomicBoolean();                      // 7.2.9.4 (2020/11/20) volatile boolean の代替え
067
068        /** 7.2.5.0 (2020/06/01) MainProcess は、シングルインスタンスとして扱います。 */
069        private static MainProcess mainPrcs ;
070
071        // 7.2.9.4 (2020/11/20) staticレベルのロック
072        private static final Object STATIC_LOCK = new Object();
073
074        private final ConcurrentMap<String,FileExec> execMap = new ConcurrentHashMap<>();               // キーは、systemId + rsrv_no
075
076        private int cnt ;
077
078        /**
079         * デフォルトコンストラクターをprivateにして、
080         * オブジェクトの生成をさせないようにする。
081         *
082         * @og.rev 7.2.5.0 (2020/06/01) シングルインスタンス
083         */
084//      public MainProcess() { super(); }                       // 必要ないが、とりあえず。
085        private MainProcess() {
086                LOGGER.info( () -> "MainProcess Start! " );
087        }
088
089        /**
090         * MainProcess は、シングルインスタンスです。
091         *
092         * 既存のインスタンスか、新しいインスタンスを作成して返します。
093         * serverフォルダ は必須です。
094         *
095         * @og.rev 7.2.5.0 (2020/06/01) シングルインスタンス
096         * @og.rev 7.2.9.4 (2020/11/20) staticレベルのロック
097         *
098         * @return      新しいインスタンス または、既存のインスタンス
099         */
100//      synchronized public static MainProcess getInstance() {
101        public static MainProcess getInstance() {
102                synchronized( STATIC_LOCK ) {
103                        if( mainPrcs == null ) {
104                                mainPrcs = new MainProcess();
105                        }
106                }
107
108                return mainPrcs;
109        }
110
111        /**
112         * 開始処理を行います。
113         *
114         * 内部で自身のインスタンスを作成して、ScheduledExecutorService で繰り返し実行します。
115         *
116         * @og.rev 7.2.5.0 (2020/06/01) シングルインスタンス
117         * @og.rev 7.2.9.4 (2020/11/20) static final の大文字化
118         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
119         * @og.rev 7.2.9.4 (2020/11/20) staticレベルのロック
120         */
121//      synchronized public static void start() {
122        public static void start() {
123                try {
124//                      if( futureSet.isEmpty() ) {                                                     // 何度でも実行できるように
125                        if( FUTURE_SET.isEmpty() ) {                                            // 何度でも実行できるように
126//                              final MainProcess mainPrcs = getInstance();
127                                final MainProcess localPrcs ;
128                                synchronized( STATIC_LOCK ) {                                   // 7.2.9.4 (2020/11/20) staticレベルのロック
129                                        localPrcs = getInstance();
130                                }
131
132                                // ********** 【初期値定義】 **********
133                                final String loopStr = HybsConst.getenv( "loop"  );     // スキャンするインターバル(秒)
134                                final long   loopSec = loopStr == null || loopStr.isEmpty() ? 30L : Long.parseLong( loopStr );          // スキャンするインターバル(秒)
135
136                //              // 一定時間ごとに処理を実行                                   開始タスク  , 初回遅延 , 繰返間隔 , スケール(秒)
137                //              futureSet.add( scheduler.scheduleAtFixedRate( localPrcs , 0        , loopSec  , TimeUnit.SECONDS ) );
138
139                                // エラー時に繰り返し間隔より長く待機させる。
140                                // 処理の完了を待ってから一定時間待機                     開始タスク  , 初回遅延 , 待機時間 , スケール(秒)
141//                              futureSet.add( scheduler.scheduleWithFixedDelay( localPrcs , 0        , loopSec  , TimeUnit.SECONDS ) );
142                                FUTURE_SET.add( SCHEDULER.scheduleWithFixedDelay( localPrcs , 0        , loopSec  , TimeUnit.SECONDS ) );               // 7.2.9.4 (2020/11/20)
143
144//                              isStart = true;
145                                IS_START.set( true );           // 7.2.9.4 (2020/11/20)
146                        }
147                }
148                catch( final Throwable th ) {
149                        // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
150                        final String errMsg = "MainProcess#start" ;
151                        LOGGER.warning( th , "MSG0021" , errMsg );
152
153                        shutdown( false );              // エラーなので再実行できるようにしておきます。
154                }
155        }
156
157        /**
158         * 終了処理を行います。
159         *
160         * @og.rev 7.2.5.0 (2020/06/01) シングルインスタンス
161         * @og.rev 7.2.9.4 (2020/11/20) static final の大文字化
162         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
163         * @og.rev 7.2.9.4 (2020/11/20) staticレベルのロック
164         *
165         * @param       flag 完全終了時は true を設定する。
166         */
167//      synchronized public static void shutdown( final boolean flag ) {
168        public static void shutdown( final boolean flag ) {
169                LOGGER.info( () -> "MainProcess Shutdown Starting ..." );
170//              isStart = false;
171                IS_START.set( false );          // 7.2.9.4 (2020/11/20)
172
173//              futureSet.forEach( fu -> fu.cancel( true ) );           // 実行中のタスクに割り込む
174//              futureSet.clear();                                                                      // 初期化しておく
175
176                FUTURE_SET.forEach( fu -> fu.cancel( true ) );          // 7.2.9.4 (2020/11/20) 実行中のタスクに割り込む
177                FUTURE_SET.clear();                                                                     // 7.2.9.4 (2020/11/20) 初期化しておく
178
179                if( flag ) {
180//                      scheduler.shutdownNow();                                                // trueの場合、再実行できなくなる。
181                        SCHEDULER.shutdownNow();                                                // 7.2.9.4 (2020/11/20) trueの場合、再実行できなくなる。
182                }
183
184                synchronized( STATIC_LOCK ) {                                           // 7.2.9.4 (2020/11/20) staticレベルのロック
185                        if( mainPrcs != null ) {
186                                // 必要ないかもしれませんが、正常終了させます。
187                                mainPrcs.watchStop();
188                        }
189                        mainPrcs = null;
190                }
191
192                LOGGER.info( () -> "MainProcess Shutdown Complete." );
193        }
194
195        /**
196         * MainProcess の処理が起動しているかどうかを返します。
197         *
198         * @og.rev 7.2.5.0 (2020/06/01) 新規追加
199         * @og.rev 7.2.9.4 (2020/11/20) PMD:volatile boolean の代替え。
200         *
201         * @return      true:起動中/false:停止中
202         */
203        public static boolean isStarted() {
204//              return isStart ;
205                return IS_START.get();          // 7.2.9.4 (2020/11/20)
206        }
207
208        /**
209         * 時間起動のタスクオブジェクトを起動します。
210         *
211         * コマンドリストは、予約番号,種別,号機指定,雛形ファイル,開始日時,実行間隔,終了日時,経過終了間隔,パラメータMAP を
212         * 文字列として順番に持っています。
213         * リストの数が、少ない場合は、それぞれ、初期値が使用されます。
214         * 最低、コマンド種別は、必要です。
215         *
216         * @param       cmndLine        CommandLineオブジェクト
217         */
218        private void startTask( final CommandLine cmndLine ) {
219                // タスクオブジェクトの起動前に、一旦過去の依頼は停止しておきます。
220                final String systemId   = cmndLine.getValue( GE70.SYSTEM_ID );          // システムID
221                final String rsrvNo             = cmndLine.getValue( GE70.RSRV_NO );            // 予約番号
222                final String mapKey             = systemId + "_" + rsrvNo ;
223                stopTask( mapKey );                                                                                                     // 一旦、すべてを停止します。
224
225                // ※ 取込予約フラグ(FGYKAN)は、DB検索時に、1:実行 の場合のみ起動する。
226                final String fgkan = cmndLine.getValue( GE70.FGYKAN );                          // 取込予約フラグ 1:実行 2:停止
227                if( "1".equals( fgkan ) ) {                                                                                     // 1:実行 以外は、先に停止されている。
228                        final FileExec fExec = new FileExec( cmndLine );
229
230                        LOGGER.info( () -> "startTask: yoyakuNo=[" + mapKey + "]" );
231
232                        fExec.watchStart();
233                        execMap.put( mapKey,fExec );
234                }
235                else {
236                        LOGGER.warning( () -> "【WARNING】startTask: yoyakuNo=[" + mapKey + "] , fgkan=[" + fgkan + "]" );                // 6.8.1.5 (2017/09/08)
237                }
238        }
239
240        /**
241         * 時間起動のタスクオブジェクトをキャンセルします。
242         *
243         * @param       mapKey          コマンド予約番号時のキーワード
244         */
245        private void stopTask( final String mapKey ) {
246                final FileExec  fExec = execMap.remove( mapKey );               // 取り消しなので、Mapから削除します。
247                if( fExec != null ) {                                                                   // 完了(正常終了、例外、取り消し)以外は、キャンセルします。
248                        fExec.watchStop();
249                }
250
251                LOGGER.info( () -> "stopTask: yoyakuNo=[" + mapKey + "]" );
252        }
253
254        /**
255         * すべての成形機のセッションフォルダの監視を終了します。
256         *
257         */
258        public void watchStop() {
259                execMap.forEach( (no,fExec) -> fExec.watchStop() );
260        }
261
262        /**
263         * Runnableインターフェースのrunメソッドです。
264         *
265         * ScheduledExecutorService で繰り返し実行させるので、Throwable 全てのを拾う。
266         *
267         * @og.rev 7.2.5.0 (2020/06/01) TomcatのServletContextListenerから実行できるように修正します。
268         */
269        @Override       // Runnable
270        public void run() {
271                try {
272                        final List<CommandLine> cmndList = CommandLine.dbCommand();
273                        cmndList.forEach( cmndLine -> startTask( cmndLine ) );
274                        System.out.println( StringUtil.getTimeFormat( "yyyy/MM/dd HH:mm:ss [" + (cnt++) + "]" ) );      // 6.8.1.5 (2017/09/08)
275                }
276                catch( final Throwable th ) {
277                        // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
278                        final String errMsg = "MainProcess#run" ;
279                        LOGGER.warning( th , "MSG0021" , errMsg );
280
281                        shutdown( true );               // 完全終了
282
283                        // エラーの場合は、少し待って終了します。
284//                      try{ Thread.sleep( WAIT_TIME ); } catch( final InterruptedException ex ){}
285                        try { Thread.sleep( WAIT_TIME ); } catch( final InterruptedException ignored ) {}       // 8.5.4.2 (2024/01/12) PMD 7.0.0 EmptyCatchBlock
286                }
287        }
288
289//      /**
290//       * ファイル取込システムのメインクラスを起動します。
291//       *
292//       * <pre>
293//       * システムプロパティー定義(例:初期値)
294//       *
295//       * [-Dloop="10"]        : データベースの状態をチェックする間隔(秒)。初期値は、10秒です。
296//       * </pre>
297//       *
298//       * @og.rev 7.2.5.0 (2020/06/01) TomcatのServletContextListenerから実行できるように修正します。
299//       *
300//       * @param       args 引数配列
301//       */
302//      public static void main( final String[] args ) {
303//              try {
304//                      MainProcess.start();
305//              }
306//              catch( final Throwable th ) {
307//                      // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
308//                      final String errMsg = "MainProcess#main" ;
309//                      LOGGER.warning( th , "MSG0021" , errMsg );
310//
311//                      MainProcess.shutdown( true );                   // 完全終了処理
312//                      System.exit( 1 );                                               // 強制終了します。
313//              }
314//
315//              // 仮想マシンのシャットダウン・フックを登録
316//              final Thread shutdownHook = new Thread( "MainProcess" ) {
317//                      /**
318//                       * シャットダウン・フックの実行メソッドです。
319//                       */
320//                      @Override
321//                      public void run() {
322//                              MainProcess.shutdown( true );
323//                      }
324//              };
325//              Runtime.getRuntime().addShutdownHook( shutdownHook );
326//      }
327}