    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.util.Argument;
    019import org.opengion.fukurou.util.StringUtil;
    020import org.opengion.fukurou.util.FileUtil;
    021import org.opengion.fukurou.util.LogWriter;
    022
    023import java.util.List;
    024import java.util.ArrayList;
    025import java.util.Date;
    026
    027/**
    028 * MainProcess は、HybsProcess を継承した、ParamProcess,FirstProcess,ChainProcess
    029 * の実装クラスを実行するメインメソッドを持つクラスです。
    030 * ParamProcess は、唯一 最初に定義できるクラスで、データベース接続やエラーメール
    031 * などの共通なパラメータを定義します。なくても構いません。
    032 * FirstProcess は、処理を実行する最初のクラスで、このクラスでデータが作成されます。
    033 * ループ処理は、この FirstProcess で順次作成された LineModel オブジェクトを
    034 * １行づつ下位の ChainProcess に流していきます。
    035 * ChainProcess は、FirstProcess で作成されたデータを、受け取り、処理します。
    036 * 処理対象から外れる場合は、LineModel を null に設定する為、下流には流れません。
    037 * フィルタチェインの様に使用します。なくても構いませんし、複数存在しても構いません。
    038 *
    039 * このクラスは、Runnable インターフェースを実装しています。
    040 *
    041 * 各実装クラスに引数を指定する場合は、-キー=値 形式で指定します。
    042 * キーと値の間には、スベースを入れないで下さい。
    043 * 先頭が - なら引数。 # ならコメント になります。
    044 * - でも # でもない引数は、HybsProcess のサブクラスになります。
    045 *
    046 *  Usage: java MainProcess サブChainProcessクラス [[-キー=値] ･･･] [･･･]
    047 *    [ParamProcess実装クラス ]：ParamProcess を実装したクラス
    048 *       -キー=値              ：各サブクラス毎の引数。 - で始まり、= で分割します。
    049 *       -AAA=BBB              ：引数は、各クラス毎に独自に指定します。
    050 *     FirstProcess実装クラス  ：FirstProcess を実装したクラス
    051 *       -キー=値              ：各サブクラス毎の引数。 - で始まり、= で分割します。
    052 *       -AAA=BBB              ：引数は、各クラス毎に独自に指定します。
    053 *       #-AAA=BBB             ：先頭が - なら引数。 # ならコメント になります。
    054 *    [ChainProcess実装クラス1]：ChainProcess を実装したクラス：複数指定できます。
    055 *       -CCC=DDD
    056 *    [ChainProcess実装クラス2]：ChainProcess を実装したクラス：複数指定できます。
    057 *       -EEE=FFF
    058 *
    059 * @version  4.0
    060 * @author   Kazuhiko Hasegawa
    061 * @since    JDK5.0,
    062 */
    063public final class MainProcess implements Runnable {
    064        private static final String CR = System.getProperty("line.separator");
    065
    066        /** main 処理のリターン値  初期化 {@value} */
    067        public static final int RETURN_INIT = -1;
    068        /** main 処理のリターン値  正常値 {@value} */
    069        public static final int RETURN_OK = 0;
    070        /** main 処理のリターン値  正常値 {@value} */
    071        public static final int RETURN_WARN = 1;
    072        /** main 処理のリターン値  異常値 {@value} */
    073        public static final int RETURN_NG = 2;
    074
    075        private List<HybsProcess> list = null;
    076        private ParamProcess  param  = null;
    077        private LoggerProcess logger = null;
    078        private int kekka = RETURN_INIT;
    079
    080        /**
    081         * HybsProcess クラスを管理しているリストをセットします。
    082         *
    083         * 引数のListオブジェクトは、浅いコピーで、取り込みます。
    084         *
    085         * @param       list    HybsProcessリスト
    086         * @throws IllegalArgumentException 引数が、null の場合。
    087         */
    088        public void setList( final List<HybsProcess> list ) {
    089                if( list == null ) {
    090                        String errMsg = "引数の List に、null は設定できません。" ;
    091                        throw new IllegalArgumentException( errMsg );
    092                }
    093                this.list = new ArrayList<HybsProcess>( list );
    094        }
    095
    096        /**
    097         * HybsProcess クラスを初期化します。
    098         *
    099         * 主に、ParamProcess クラスの取り出し(または、作成)処理を分離しています。
    100         *
    101         * @og.rev 5.1.5.0 (2010/04/01) 出力が２重、３重に出力されるのを回避します。
    102         */
    103        private void init() {
    104                if( list == null ) {
    105                        String errMsg = "リスト が null です。まず、setList( List<HybsProcess> ) が必要です。";
    106                        throw new RuntimeException( errMsg );
    107                }
    108
    109                try {
    110                        // List の最上位は、必ず、LoggerProcess を配備する。
    111                        HybsProcess process = list.get(0);
    112                        if( process instanceof LoggerProcess ) {
    113                                logger = (LoggerProcess)process;
    114                                logger.init( null );
    115                                list.remove(0);                 // List上から、LoggerProcess を削除しておきます。
    116                                process = list.get(0);  // 次の取得を行っておく。プログラムの都合
    117                        }
    118                        else {
    119                                logger = new Process_Logger();
    120                                logger.putArgument( "logFile"  , "System.out" );
    121                                logger.putArgument( "dispFile" , "System.out" );
    122                                logger.init( null );
    123                        }
    124
    125                        // その次は、ParamProcess かどうかをチェック
    126                        if( process instanceof ParamProcess ) {
    127                                param = (ParamProcess)process;
    128                                param.setLoggerProcess( logger );
    129                                param.init( null );
    130                                list.remove(0);                 // List上から、ParamProcess を削除しておきます。
    131                        }
    132                }
    133                catch (Throwable th) {
    134                        StringBuilder errMsg = new StringBuilder();
    135                        errMsg.append( "初期化中に例外が発生しました。" ).append( CR );
    136                        errMsg.append( th.getMessage() ) ;
    137                        String errStr = errMsg.toString();
    138
    139                        logger.errLog( errStr,th );
    140                        LogWriter.log( errStr );
    141                        // 5.1.5.0 (2010/04/01) 出力が２重、３重に出力されるのを回避します。
    142//                      System.out.println( errStr );
    143
    144                        if( param  != null ) { param.end( false ); }
    145                        logger.end( false );
    146
    147                        throw new RuntimeException( 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                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                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                        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 (Throwable th) {
    204                        kekka = RETURN_NG;
    205
    206                        StringBuilder errMsg = new StringBuilder();
    207                        errMsg.append( CR );    // 5.1.5.0 (2010/04/01) 先に改行しておきます。
    208                        errMsg.append( "データ処理中に例外が発生しました。 [" );
    209                        errMsg.append( rowNo ).append( "]行目" ).append( CR );
    210                        errMsg.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                                HybsProcess process = list.get(i);
    216                                errMsg.append( process.toString() );
    217                        }
    218                        String errStr = errMsg.toString();
    219                        logger.errLog( errStr,th );
    220                        LogWriter.log( errStr );
    221                        // 5.1.5.0 (2010/04/01) 出力が２重、３重に出力されるのを回避します。
    222//                      System.out.println( errStr );
    223//                      throw new RuntimeException( errStr,th );        // 4.0.0 (2005/01/31)
    224                }
    225                finally {
    226                        // 終了 必ず全ての endメソッドをコールします。
    227                        logger.logging( "終了処理を行います。" );
    228                        StringBuilder buf = new StringBuilder();
    229                        // 5.3.4.0 (2011/04/01) ロガーのreport()を呼びます。(タイトルを追加)
    230                        if( param != null ) {
    231                                buf.append( logger.report() ).append( CR );
    232                                buf.append( param.report() );
    233                        }
    234
    235                        boolean isOK = (kekka == RETURN_OK);
    236                        for( int i=0; i<cnt; i++ ) {
    237                                HybsProcess process = list.get(i);
    238                                if( process != null ) {
    239                                        buf.append( CR ).append( process.report() );
    240                                        process.end( isOK );
    241                                }
    242                        }
    243                        // 一番最後に、ParamProcess を終了します。
    244                        if( param  != null ) { param.end( isOK ); }             // 5.5.4.5 (2012/07/27) 一連のProcessの end() の最後にします。
    245
    246                        buf.append( CR );
    247                        logger.logging( buf.toString() );
    248                        logger.logging( "実行結果は、[" + errCode(kekka) + "] です。" );
    249                        long ed = System.currentTimeMillis();
    250                        logger.logging( "合計処理時間 = " + (ed-st) + " (ms) です。" );
    251                        logger.logging( new Date( ed ) + " 終了しました。" );
    252
    253//                      // 一番最後に、ParamProcess を終了します。
    254//                      if( param  != null ) { param.end( isOK ); }
    255                        logger.end( isOK );
    256                }
    257        }
    258
    259        /**
    260         * 処理の実行結果を返します。
    261         *
    262         * @return      実行結果
    263         * @see #RETURN_INIT
    264         */
    265        public int getKekka() { return kekka; }
    266
    267        /**
    268         * 処理を行うメインメソッドです。
    269         *
    270         * @og.rev 4.0.0.0 (2007/11/22) ConnDataFactory の使用を廃止
    271         *
    272         * @param       args    コマンド引数配列
    273         */
    274        public static void main( final String[] args ) {
    275                if( args.length == 0 ) {
    276                        LogWriter.log( usage() );
    277                        return ;
    278                }
    279
    280                // 引数の加工
    281                List<HybsProcess> list = makeHybsProcessList( args );
    282
    283                // 特別に、LoggerProcess がなければ、標準出力を使用するロガーを登録する。
    284                HybsProcess prcs = list.get(0);
    285                if( ! (prcs instanceof LoggerProcess) ) {
    286                        LoggerProcess logger = new Process_Logger();
    287                        logger.setDisplayWriter( FileUtil.getLogWriter( "System.out" ) );
    288                        list.add( 0,logger );
    289                }
    290
    291                // 引数リスト(HybsProcessリスト)を登録
    292                MainProcess process = new MainProcess();
    293                process.setList( list );
    294
    295                // 処理の実行開始
    296                process.run();
    297        }
    298
    299        /**
    300         * メインに渡された引数配列 より、各 ChainProcess インスタンス を作成します。
    301         *
    302         * @param       args    メインに渡された引数配列
    303         *
    304         * @return      ChainProcessインスタンスのList
    305         */
    306        private static List<HybsProcess> makeHybsProcessList( final String[] args ) {
    307                ArrayList<HybsProcess> list = new ArrayList<HybsProcess>();
    308
    309                HybsProcess process = null;
    310                Argument argment = new Argument( MainProcess.class.getName() );
    311                for( int i=0; i<args.length; i++ ) {
    312                        int type = argment.getArgumentType( args[i] ) ;
    313
    314                        switch( type ) {
    315                                case Argument.CMNT : continue;
    316                                case Argument.ARGS :
    317                                        process = (HybsProcess)StringUtil.newInstance( args[i] );
    318                                        list.add( process );
    319                                        break;
    320                                case Argument.PROP :
    321                                        if( process != null ) {
    322                                                process.putArgument( args[i] );
    323                                        }
    324                                        break;
    325                                default: break;
    326                        }
    327                }
    328                return list;
    329        }
    330
    331        /**
    332         * エラーコードに対するメッセージを返します。
    333         *
    334         * @param       code    エラーコード
    335         *
    336         * @return      エラーコードに対するメッセージ
    337         */
    338        public String errCode( final int code ) {
    339                final String errMsg ;
    340                switch( code ) {
    341                        case RETURN_INIT : errMsg = "初期化" ; break;
    342                        case RETURN_OK   : errMsg = "正常" ; break;
    343                        case RETURN_WARN : errMsg = "警告" ; break;
    344                        case RETURN_NG   : errMsg = "異常" ; break;
    345                        default :errMsg = "未定義エラー" ; break;
    346                }
    347                return errMsg ;
    348        }
    349
    350        /**
    351         * このクラスの使用方法を返します。
    352         *
    353         * @return      このクラスの使用方法
    354         */
    355        private static String usage() {
    356
    357                StringBuilder buf = new StringBuilder();
    358
    359                buf.append( "ChainProcess を実装した各クラスを、順次実行します。" ).append( CR );
    360                buf.append( "キーと値の間には、スベースを入れないで下さい。").append( CR ).append( CR );
    361
    362                buf.append( "Usage: java MainProcess サブChainProcessクラス [[-キー=値] ･･･] [･･･]  " ).append( CR );
    363                buf.append( "   サブChainProcessクラス ：ChainProcess を実装したクラス" ).append( CR );
    364                buf.append( "     -キー=値             ：各サブクラス毎の引数。 - で始まり、= で分割します。" ).append( CR );
    365                buf.append( "     -AAA=BBB             ：複数指定できます。" ).append( CR );
    366                buf.append( "   サブChainProcessクラス ：複数指定できます。" ).append( CR );
    367                buf.append( "     -CCC=DDD " ).append( CR );
    368
    369                return buf.toString();
    370        }
    371}