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 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) 出力が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
142                        // 5.1.5.0 (2010/04/01) 出力が2重、3重に出力されるのを回避します。
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) 出力が2重、3重に出力されるのを回避します。
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}