    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       // Runnable
    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}