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