    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        @Override       // Runnable
    160        public void run() {
    161                init();
    162
    163                final long st = System.currentTimeMillis();
    164                logger.logging( "=================================================================" );
    165                logger.logging( new Date( st ) + " 処理を開始します。" );
    166                logger.logging( getClass().getName() );
    167
    168                kekka = RETURN_NG;
    169                LineModel model = null;
    170                int rowNo = 0;
    171                final int cnt = list.size();
    172                try {
    173                        // 初期化 途中でエラーが発生すれば、終了します。
    174                        logger.logging( "初期化処理を行います。" );
    175        //              if( param != null ) { logger.logging( param.toString() ); }
    176
    177                        // List には、FirstProcess と ChainProcess のみ存在する。
    178                        HybsProcess process ;
    179                        for( int i=0; i<cnt; i++ ) {
    180                                process = list.get(i);
    181                                process.setLoggerProcess( logger );
    182                                process.init( param );
    183        //                      logger.logging( process.toString() );
    184                        }
    185
    186                        logger.logging( "Process を実行します。" );
    187                        final FirstProcess firstProcess  = (FirstProcess)list.get(0);
    188                        ChainProcess chainProcess ;
    189                        while( firstProcess.next() ) {
    190                                model = firstProcess.makeLineModel( rowNo );
    191                                for( int i=1; i<cnt && model != null ; i++ ) {
    192                                        chainProcess = (ChainProcess)list.get(i);
    193                                        model = chainProcess.action( model );
    194                                }
    195                                rowNo++;
    196                                // 5.1.2.0 (2010/01/01) 実行中の経過表示を、標準出力ではなく、エラー出力に変更します。
    197                                if( rowNo%50   == 0 ) { System.err.print( "." ); }
    198                                if( rowNo%1000 == 0 ) { System.err.println( "  Count=[" + rowNo + "]" ); }
    199                        }
    200                        kekka = RETURN_OK;
    201                        logger.logging(     "  Total=[" + rowNo + "]" );
    202                        System.err.println( "  Total=[" + rowNo + "]" );                // 5.5.4.5 (2012/07/27) 処理の最後に結果を出力します。
    203                }
    204                catch( final Throwable th) {
    205                        kekka = RETURN_NG;
    206
    207                        final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE )
    208                                .append( CR )   // 5.1.5.0 (2010/04/01) 先に改行しておきます。
    209                                .append( "データ処理中に例外が発生しました。 [" )
    210                                .append( rowNo ).append( "]行目" ).append( CR )
    211                                .append( th.getMessage() ).append( CR ) ;
    212
    213                        if( model != null ) { errMsg.append( model.toString() ).append( CR ) ; }
    214
    215                        for( int i=0; i<cnt; i++ ) {
    216                                final HybsProcess process = list.get(i);
    217                                errMsg.append( process.toString() );
    218                        }
    219                        final String errStr = errMsg.toString();
    220                        logger.errLog( errStr,th );
    221                        LogWriter.log( errStr );
    222                }
    223                finally {
    224                        // 終了 必ず全ての endメソッドをコールします。
    225                        logger.logging( "終了処理を行います。" );
    226                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    227                        // 5.3.4.0 (2011/04/01) ロガーのreport()を呼びます。(タイトルを追加)
    228                        if( param != null ) {
    229                                buf.append( logger.report() ).append( CR );
    230                                buf.append( param.report() );
    231                        }
    232
    233                        final boolean isOK = kekka == RETURN_OK;
    234                        for( int i=0; i<cnt; i++ ) {
    235                                final HybsProcess process = list.get(i);
    236                                if( process != null ) {
    237                                        buf.append( CR ).append( process.report() );
    238                                        process.end( isOK );
    239                                }
    240                        }
    241                        // 一番最後に、ParamProcess を終了します。
    242                        if( param  != null ) { param.end( isOK ); }             // 5.5.4.5 (2012/07/27) 一連のProcessの end() の最後にします。
    243
    244                        buf.append( CR );
    245                        logger.logging( buf.toString() );
    246                        logger.logging( "実行結果は、[" + errCode(kekka) + "] です。" );
    247                        final long ed = System.currentTimeMillis();
    248                        logger.logging( "合計処理時間 = " + (ed-st) + " (ms) です。" );
    249                        logger.logging( new Date( ed ) + " 終了しました。" );
    250
    251                        // 一番最後に、ParamProcess を終了します。
    252                        logger.end( isOK );
    253                }
    254        }
    255
    256        /**
    257         * 処理の実行結果を返します。
    258         *
    259         * @return      実行結果
    260         * @see #RETURN_INIT
    261         */
    262        public int getKekka() { return kekka; }
    263
    264        /**
    265         * 処理を行うメインメソッドです。
    266         *
    267         * @og.rev 4.0.0.0 (2007/11/22) ConnDataFactory の使用を廃止
    268         * @og.rev 6.3.1.1 (2015/07/10) LoggerProcessがない場合は、本体で追加しているので、処理を削除します。
    269         * @og.rev 7.1.0.1 (2020/02/07) ExecutorService などのスレッドの終了処理を誤ると、mainメソッドが終了しない場合の対策。
    270         *
    271         * @param       args    コマンド引数配列
    272         */
    273        public static void main( final String[] args ) {
    274                if( args.length == 0 ) {
    275                        LogWriter.log( usage() );
    276                        return ;
    277                }
    278
    279                // 引数の加工
    280                final List<HybsProcess> list = makeHybsProcessList( args );
    281
    282                // 引数リスト(HybsProcessリスト)を登録
    283                final MainProcess process = new MainProcess();
    284                process.setList( list );
    285
    286                // 処理の実行開始
    287                process.run();
    288
    289                System.exit( process.getKekka() );                      // 7.1.0.1 (2020/02/07)
    290        }
    291
    292        /**
    293         * メインに渡された引数配列 より、各 ChainProcess インスタンス を作成します。
    294         *
    295         * @param       args    メインに渡された引数配列(可変長引数)
    296         *
    297         * @return      ChainProcessインスタンスのList
    298         */
    299        private static List<HybsProcess> makeHybsProcessList( final String... args ) {
    300                final ArrayList<HybsProcess> list = new ArrayList<>();
    301
    302                HybsProcess process = null;
    303                final Argument argment = new Argument( MainProcess.class.getName() );
    304                for( int i=0; i<args.length; i++ ) {
    305                        final int type = argment.getArgumentType( args[i] ) ;
    306
    307                        switch( type ) {
    308                                case Argument.CMNT : break;                             // 6.0.2.5 (2014/10/31) break追記
    309                                case Argument.ARGS :
    310                                        process = (HybsProcess)StringUtil.newInstance( args[i] );
    311                                        list.add( process );
    312                                        break;
    313                                case Argument.PROP :
    314                                        if( process != null ) {
    315                                                process.putArgument( args[i] );
    316                                        }
    317                                        break;
    318                                default: break;
    319                        }
    320                }
    321                return list;
    322        }
    323
    324        /**
    325         * エラーコードに対するメッセージを返します。
    326         *
    327         * @param       code    エラーコード
    328         *
    329         * @return      エラーコードに対するメッセージ
    330         */
    331        public String errCode( final int code ) {
    332                final String errMsg ;
    333                switch( code ) {
    334                        case RETURN_INIT : errMsg = "初期化" ; break;
    335                        case RETURN_OK   : errMsg = "正常" ; break;
    336                        case RETURN_WARN : errMsg = "警告" ; break;
    337                        case RETURN_NG   : errMsg = "異常" ; break;
    338                        default :errMsg = "未定義エラー" ; break;
    339                }
    340                return errMsg ;
    341        }
    342
    343        /**
    344         * このクラスの使用方法を返します。
    345         *
    346         * @return      このクラスの使用方法
    347         * @og.rtnNotNull
    348         */
    349        private static String usage() {
    350
    351                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    352                        .append( "ChainProcess を実装した各クラスを、順次実行します。" ).append( CR )
    353                        .append( "キーと値の間には、スベースを入れないで下さい。").append( CR ).append( CR )
    354                        .append( "Usage: java org.opengion.fukurou.process.MainProcess サブChainProcessクラス [[-キー=値] ･･･] [･･･]  " ).append( CR )
    355                        .append( "   サブChainProcessクラス ：ChainProcess を実装したクラス" ).append( CR )
    356                        .append( "     -キー=値             ：各サブクラス毎の引数。 - で始まり、= で分割します。" ).append( CR )
    357                        .append( "     -AAA=BBB             ：複数指定できます。" ).append( CR )
    358                        .append( "   サブChainProcessクラス ：複数指定できます。" ).append( CR )
    359                        .append( "     -CCC=DDD " ).append( CR );
    360
    361                return buf.toString();
    362        }
    363}