    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}