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}