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)
029
030import java.util.concurrent.atomic.AtomicBoolean;                       // 7.2.9.4 (2020/11/20) volatile boolean の代替え
031
032import org.opengion.fukurou.system.HybsConst;                           // 7.2.5.0 (2020/06/01)
033
034import static org.opengion.fukurou.fileexec.CommandLine.GE70;           // enum を簡素化して使用するための定義
035
036/**
037 * MainProcess は、単独で使用する ファイル取込システムのメインクラスです。
038 *
039 *<pre>
040 * このクラスのmainメソッドから起動します。
041 * コマンドラインを処理することで、各種処理を実行します。
042 *
043 *</pre>
044 *
045 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
046 * @og.rev 7.2.5.0 (2020/06/01) TomcatのServletContextListenerから実行できるように修正
047 *
048 * @version  7.0
049 * @author   Kazuhiko Hasegawa
050 * @since    JDK1.8,
051 */
052// public final class MainProcess {
053public final class MainProcess implements Runnable {
054        private static final XLogger LOGGER= XLogger.getLogger( MainProcess.class.getSimpleName() );    // ログ出力
055
056        /** 7.2.5.0 (2020/06/01) エラーの場合、リロードするが、その待機時間 {@value}(秒) */
057        public static final long WAIT_TIME = 30 * 1000L;
058
059//      private static final ScheduledExecutorService   scheduler = Executors.newScheduledThreadPool(5);        // 1.5.0 (2020/04/01)
060        private static final ScheduledExecutorService   SCHEDULER = Executors.newScheduledThreadPool(5);        // 7.2.9.4 (2020/11/20) 1.5.0 (2020/04/01)
061//      private static final Set<ScheduledFuture<?>>    futureSet = new HashSet<>();
062        private static final Set<ScheduledFuture<?>>    FUTURE_SET = Collections.newSetFromMap( new ConcurrentHashMap<ScheduledFuture<?>, Boolean>() ); // 7.2.9.4 (2020/11/20)
063
064
065        /** 7.2.5.0 (2020/06/01) 開始しているかどうかを確認するための状態変数 */
066//      private static volatile boolean isStart ;
067        private static final AtomicBoolean IS_START = new AtomicBoolean();                      // 7.2.9.4 (2020/11/20) volatile boolean の代替え
068
069        /** 7.2.5.0 (2020/06/01) MainProcess は、シングルインスタンスとして扱います。 */
070        private static MainProcess mainPrcs ;
071
072        // 7.2.9.4 (2020/11/20) staticレベルのロック
073        private static final Object STATIC_LOCK = new Object();
074
075        private final ConcurrentMap<String,FileExec> execMap = new ConcurrentHashMap<>();               // キーは、systemId + rsrv_no
076
077        private int cnt ;
078
079        /**
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
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                }
286        }
287
288//      /**
289//       * ファイル取込システムのメインクラスを起動します。
290//       *
291//       * <pre>
292//       * システムプロパティー定義(例:初期値)
293//       *
294//       * [-Dloop="10"]        : データベースの状態をチェックする間隔(秒)。初期値は、10秒です。
295//       * </pre>
296//       *
297//       * @og.rev 7.2.5.0 (2020/06/01) TomcatのServletContextListenerから実行できるように修正します。
298//       *
299//       * @param       args 引数配列
300//       */
301//      public static void main( final String[] args ) {
302//              try {
303//                      MainProcess.start();
304//              }
305//              catch( final Throwable th ) {
306//                      // MSG0021 = 予期せぬエラーが発生しました。\n\tメッセージ=[{0}]
307//                      final String errMsg = "MainProcess#main" ;
308//                      LOGGER.warning( th , "MSG0021" , errMsg );
309//
310//                      MainProcess.shutdown( true );                   // 完全終了処理
311//                      System.exit( 1 );                                               // 強制終了します。
312//              }
313//
314//              // 仮想マシンのシャットダウン・フックを登録
315//              final Thread shutdownHook = new Thread( "MainProcess" ) {
316//                      /**
317//                       * シャットダウン・フックの実行メソッドです。
318//                       */
319//                      @Override
320//                      public void run() {
321//                              MainProcess.shutdown( true );
322//                      }
323//              };
324//              Runtime.getRuntime().addShutdownHook( shutdownHook );
325//      }
326}