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