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 * 1行づつ下位の 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) 出力が2重、3重に出力されるのを回避します。
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) 出力が2重、3重に出力されるのを回避します。
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) 出力が2重、3重に出力されるのを回避します。
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) 出力が2重、3重に出力されるのを回避します。
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}