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