001/* 002 * Copyright (c) 2009 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.process; 017 018import java.util.List; 019import java.util.ArrayList; 020import java.util.Date; 021 022import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 023import org.opengion.fukurou.util.Argument; 024import org.opengion.fukurou.util.StringUtil; 025import org.opengion.fukurou.system.LogWriter; 026import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 027import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 028 029/** 030 * MainProcess は、HybsProcess を継承した、ParamProcess,FirstProcess,ChainProcess 031 * の実装クラスを実行するメインメソッドを持つクラスです。 032 * ParamProcess は、唯一 最初に定義できるクラスで、データベース接続やエラーメール 033 * などの共通なパラメータを定義します。なくても構いません。 034 * FirstProcess は、処理を実行する最初のクラスで、このクラスでデータが作成されます。 035 * ループ処理は、この FirstProcess で順次作成された LineModel オブジェクトを 036 * 1行づつ下位の ChainProcess に流していきます。 037 * ChainProcess は、FirstProcess で作成されたデータを、受け取り、処理します。 038 * 処理対象から外れる場合は、LineModel を null に設定する為、下流には流れません。 039 * フィルタチェインの様に使用します。なくても構いませんし、複数存在しても構いません。 040 * 041 * このクラスは、Runnable インターフェースを実装しています。 042 * 043 * 各実装クラスに引数を指定する場合は、-キー=値 形式で指定します。 044 * キーと値の間には、スベースを入れないで下さい。 045 * 先頭が - なら引数。 # ならコメント になります。 046 * - でも # でもない引数は、HybsProcess のサブクラスになります。 047 * 048 * Usage: java org.opengion.fukurou.process.MainProcess サブChainProcessクラス [[-キー=値] ・・・] [・・・] 049 * [ParamProcess実装クラス ]:ParamProcess を実装したクラス 050 * -キー=値 :各サブクラス毎の引数。 - で始まり、= で分割します。 051 * -AAA=BBB :引数は、各クラス毎に独自に指定します。 052 * FirstProcess実装クラス :FirstProcess を実装したクラス 053 * -キー=値 :各サブクラス毎の引数。 - で始まり、= で分割します。 054 * -AAA=BBB :引数は、各クラス毎に独自に指定します。 055 * #-AAA=BBB :先頭が - なら引数。 # ならコメント になります。 056 * [ChainProcess実装クラス1]:ChainProcess を実装したクラス:複数指定できます。 057 * -CCC=DDD 058 * [ChainProcess実装クラス2]:ChainProcess を実装したクラス:複数指定できます。 059 * -EEE=FFF 060 * 061 * @version 4.0 062 * @author Kazuhiko Hasegawa 063 * @since JDK5.0, 064 */ 065public final class MainProcess implements Runnable { 066 067 /** main 処理のリターン値 初期化 {@value} */ 068 public static final int RETURN_INIT = -1; 069 /** main 処理のリターン値 正常値 {@value} */ 070 public static final int RETURN_OK = 0; 071 /** main 処理のリターン値 正常値 {@value} */ 072 public static final int RETURN_WARN = 1; 073 /** main 処理のリターン値 異常値 {@value} */ 074 public static final int RETURN_NG = 2; 075 076 private List<HybsProcess> list ; 077 private ParamProcess param ; 078 private LoggerProcess logger ; 079 private int kekka = RETURN_INIT; 080 081 /** 082 * デフォルトコンストラクター 083 * 084 * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: デフォルトのコンストラクタの使用で、コメントが指定されていません 085 */ 086 public MainProcess() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 087 088 /** 089 * HybsProcess クラスを管理しているリストをセットします。 090 * 091 * 引数のListオブジェクトは、浅いコピーで、取り込みます。 092 * 093 * @param list HybsProcessリスト 094 * @throws IllegalArgumentException 引数が、null の場合。 095 */ 096 public void setList( final List<HybsProcess> list ) { 097 if( list == null ) { 098 final String errMsg = "引数の List に、null は設定できません。" ; 099 throw new IllegalArgumentException( errMsg ); 100 } 101 this.list = new ArrayList<>( list ); 102 } 103 104 /** 105 * HybsProcess クラスを初期化します。 106 * 107 * 主に、ParamProcess クラスの取り出し(または、作成)処理を分離しています。 108 * 109 * @og.rev 5.1.5.0 (2010/04/01) 出力が2重、3重に出力されるのを回避します。 110 */ 111 private void init() { 112 if( list == null ) { 113 final String errMsg = "リスト が null です。まず、setList( List<HybsProcess> ) が必要です。"; 114 throw new OgRuntimeException( errMsg ); 115 } 116 117 try { 118 // List の最上位は、必ず、LoggerProcess を配備する。 119 HybsProcess process = list.get(0); 120 if( process instanceof LoggerProcess ) { 121 logger = (LoggerProcess)process; 122 logger.init( null ); 123 list.remove(0); // List上から、LoggerProcess を削除しておきます。 124 process = list.get(0); // 次の取得を行っておく。プログラムの都合 125 } 126 else { 127 logger = new Process_Logger(); 128 logger.putArgument( "logFile" , "System.out" ); 129 logger.putArgument( "dispFile" , "System.out" ); 130 logger.init( null ); 131 } 132 133 // その次は、ParamProcess かどうかをチェック 134 if( process instanceof ParamProcess ) { 135 param = (ParamProcess)process; 136 param.setLoggerProcess( logger ); 137 param.init( null ); 138 list.remove(0); // List上から、ParamProcess を削除しておきます。 139 } 140 } 141 catch( final Throwable th) { 142 final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE ) 143 .append( "初期化中に例外が発生しました。" ).append( CR ) 144 .append( th.getMessage() ) ; 145 final String errStr = errMsg.toString(); 146 147 logger.errLog( errStr,th ); 148 LogWriter.log( errStr ); 149 150 // 5.1.5.0 (2010/04/01) 出力が2重、3重に出力されるのを回避します。 151 if( param != null ) { param.end( false ); } 152 logger.end( false ); 153 154 throw new OgRuntimeException( errStr,th ); // 4.0.0 (2005/01/31) 155 } 156 } 157 158 /** 159 * HybsProcess クラスを実行します。 160 * 161 * @og.rev 5.1.2.0 (2010/01/01) 実行中の経過表示を、標準出力ではなく、エラー出力に変更 162 * @og.rev 5.1.5.0 (2010/04/01) 出力が2重、3重に出力されるのを回避します。 163 * @og.rev 5.3.4.0 (2011/04/01) タイトル追加 164 * @og.rev 5.5.4.5 (2012/07/27) 処理の最後に結果を出力します。 165 */ 166 @Override // Runnable 167 public void run() { 168 init(); 169 170 // 8.5.5.1 (2024/02/29) spotbugs UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR 171 // 実は、init()で list の null判定を行っているので、この処理は不要なはず。 172 if( list == null ) { 173 final String errMsg = "リスト が null です。まず、setList( List<HybsProcess> ) が必要です。"; 174 throw new OgRuntimeException( errMsg ); 175 } 176 177 final long st = System.currentTimeMillis(); 178 logger.logging( "=================================================================" ); 179 logger.logging( new Date( st ) + " 処理を開始します。" ); 180 logger.logging( getClass().getName() ); 181 182 kekka = RETURN_NG; 183 LineModel model = null; 184 int rowNo = 0; 185 186 final int cnt = list.size(); 187 try { 188 // 初期化 途中でエラーが発生すれば、終了します。 189 logger.logging( "初期化処理を行います。" ); 190 // if( param != null ) { logger.logging( param.toString() ); } 191 192 // List には、FirstProcess と ChainProcess のみ存在する。 193 HybsProcess process ; 194 for( int i=0; i<cnt; i++ ) { 195 process = list.get(i); 196 process.setLoggerProcess( logger ); 197 process.init( param ); 198 // logger.logging( process.toString() ); 199 } 200 201 logger.logging( "Process を実行します。" ); 202 final FirstProcess firstProcess = (FirstProcess)list.get(0); 203 ChainProcess chainProcess ; 204 while( firstProcess.next() ) { 205 model = firstProcess.makeLineModel( rowNo ); 206 for( int i=1; i<cnt && model != null ; i++ ) { 207 chainProcess = (ChainProcess)list.get(i); 208 model = chainProcess.action( model ); 209 } 210 rowNo++; 211 // 5.1.2.0 (2010/01/01) 実行中の経過表示を、標準出力ではなく、エラー出力に変更します。 212 if( rowNo%50 == 0 ) { System.err.print( "." ); } 213 if( rowNo%1000 == 0 ) { System.err.println( " Count=[" + rowNo + "]" ); } 214 } 215 kekka = RETURN_OK; 216 logger.logging( " Total=[" + rowNo + "]" ); 217 System.err.println( " Total=[" + rowNo + "]" ); // 5.5.4.5 (2012/07/27) 処理の最後に結果を出力します。 218 } 219 catch( final Throwable th) { 220 kekka = RETURN_NG; 221 222 final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE ) 223 .append( CR ) // 5.1.5.0 (2010/04/01) 先に改行しておきます。 224 .append( "データ処理中に例外が発生しました。 [" ) 225 .append( rowNo ).append( "]行目" ).append( CR ) 226 .append( th.getMessage() ).append( CR ) ; 227 228 if( model != null ) { errMsg.append( model.toString() ).append( CR ) ; } 229 230 for( int i=0; i<cnt; i++ ) { 231 final HybsProcess process = list.get(i); 232 errMsg.append( process.toString() ); 233 } 234 final String errStr = errMsg.toString(); 235 logger.errLog( errStr,th ); 236 LogWriter.log( errStr ); 237 } 238 finally { 239 // 終了 必ず全ての endメソッドをコールします。 240 logger.logging( "終了処理を行います。" ); 241 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 242 // 5.3.4.0 (2011/04/01) ロガーのreport()を呼びます。(タイトルを追加) 243 if( param != null ) { 244 buf.append( logger.report() ).append( CR ); 245 buf.append( param.report() ); 246 } 247 248 final boolean isOK = kekka == RETURN_OK; 249 for( int i=0; i<cnt; i++ ) { 250 final HybsProcess process = list.get(i); 251 if( process != null ) { 252 buf.append( CR ).append( process.report() ); 253 process.end( isOK ); 254 } 255 } 256 // 一番最後に、ParamProcess を終了します。 257 if( param != null ) { param.end( isOK ); } // 5.5.4.5 (2012/07/27) 一連のProcessの end() の最後にします。 258 259 buf.append( CR ); 260 logger.logging( buf.toString() ); 261 logger.logging( "実行結果は、[" + errCode(kekka) + "] です。" ); 262 final long ed = System.currentTimeMillis(); 263 logger.logging( "合計処理時間 = " + (ed-st) + " (ms) です。" ); 264 logger.logging( new Date( ed ) + " 終了しました。" ); 265 266 // 一番最後に、ParamProcess を終了します。 267 logger.end( isOK ); 268 } 269 } 270 271 /** 272 * 処理の実行結果を返します。 273 * 274 * @return 実行結果 275 * @see #RETURN_INIT 276 */ 277 public int getKekka() { return kekka; } 278 279 /** 280 * 処理を行うメインメソッドです。 281 * 282 * @og.rev 4.0.0.0 (2007/11/22) ConnDataFactory の使用を廃止 283 * @og.rev 6.3.1.1 (2015/07/10) LoggerProcessがない場合は、本体で追加しているので、処理を削除します。 284 * @og.rev 7.1.0.1 (2020/02/07) ExecutorService などのスレッドの終了処理を誤ると、mainメソッドが終了しない場合の対策。 285 * 286 * @param args コマンド引数配列 287 */ 288 public static void main( final String[] args ) { 289 if( args.length == 0 ) { 290 LogWriter.log( usage() ); 291 return ; 292 } 293 294 // 引数の加工 295 final List<HybsProcess> list = makeHybsProcessList( args ); 296 297 // 引数リスト(HybsProcessリスト)を登録 298 final MainProcess process = new MainProcess(); 299 process.setList( list ); 300 301 // 処理の実行開始 302 process.run(); 303 304 System.exit( process.getKekka() ); // 7.1.0.1 (2020/02/07) 305 } 306 307 /** 308 * メインに渡された引数配列 より、各 ChainProcess インスタンス を作成します。 309 * 310 * @og.rev 8.5.5.1 (2024/02/29) switch を if 文に置き換えます。 311 * 312 * @param args メインに渡された引数配列(可変長引数) 313 * 314 * @return ChainProcessインスタンスのList 315 */ 316 private static List<HybsProcess> makeHybsProcessList( final String... args ) { 317// final ArrayList<HybsProcess> list = new ArrayList<>(); 318 final List<HybsProcess> list = new ArrayList<>(); // 8.5.4.2 (2024/01/12) PMD 7.0.0 LooseCoupling 319 320 HybsProcess process = null; 321 final Argument argment = new Argument( MainProcess.class.getName() ); 322 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 323// for( int i=0; i<args.length; i++ ) { 324// final int type = argment.getArgumentType( args[i] ) ; 325 for( final String arg : args ) { 326 final int type = argment.getArgumentType( arg ) ; 327 328 // 8.5.5.1 (2024/02/29) switch を if 文に置き換え 329// switch( type ) { 330// case Argument.CMNT : break; // 6.0.2.5 (2014/10/31) break追記 331// case Argument.ARGS : 332//// process = (HybsProcess)StringUtil.newInstance( args[i] ); 333// process = (HybsProcess)StringUtil.newInstance( arg ); 334// list.add( process ); 335// break; 336// case Argument.PROP : 337// if( process != null ) { 338//// process.putArgument( args[i] ); 339// process.putArgument( arg ); 340// } 341// break; 342// default: break; 343// } 344 if( Argument.ARGS == type ) { 345 process = (HybsProcess)StringUtil.newInstance( arg ); 346 list.add( process ); 347 } 348 else if( Argument.PROP == type && process != null ) { 349 process.putArgument( arg ); 350 } 351 } 352 return list; 353 } 354 355 /** 356 * エラーコードに対するメッセージを返します。 357 * 358 * @og.rev 8.5.5.1 (2024/02/29) switch式の使用 359 * 360 * @param code エラーコード 361 * 362 * @return エラーコードに対するメッセージ 363 */ 364 public String errCode( final int code ) { 365 // 8.5.5.1 (2024/02/29) switch式の使用 366// final String errMsg ; 367// switch( code ) { 368// case RETURN_INIT : errMsg = "初期化" ; break; 369// case RETURN_OK : errMsg = "正常" ; break; 370// case RETURN_WARN : errMsg = "警告" ; break; 371// case RETURN_NG : errMsg = "異常" ; break; 372// default :errMsg = "未定義エラー" ; break; 373// } 374// return errMsg ; 375 return switch( code ) { 376 case RETURN_INIT -> "初期化" ; 377 case RETURN_OK -> "正常" ; 378 case RETURN_WARN -> "警告" ; 379 case RETURN_NG -> "異常" ; 380 default -> "未定義エラー" ; 381 }; 382 } 383 384 /** 385 * このクラスの使用方法を返します。 386 * 387 * @return このクラスの使用方法 388 * @og.rtnNotNull 389 */ 390 private static String usage() { 391 392 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 393 .append( "ChainProcess を実装した各クラスを、順次実行します。" ).append( CR ) 394 .append( "キーと値の間には、スベースを入れないで下さい。").append( CR ).append( CR ) 395 .append( "Usage: java org.opengion.fukurou.process.MainProcess サブChainProcessクラス [[-キー=値] ・・・] [・・・] " ).append( CR ) 396 .append( " サブChainProcessクラス :ChainProcess を実装したクラス" ).append( CR ) 397 .append( " -キー=値 :各サブクラス毎の引数。 - で始まり、= で分割します。" ).append( CR ) 398 .append( " -AAA=BBB :複数指定できます。" ).append( CR ) 399 .append( " サブChainProcessクラス :複数指定できます。" ).append( CR ) 400 .append( " -CCC=DDD " ).append( CR ); 401 402 return buf.toString(); 403 } 404}