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 * 1行づつ下位の 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) 出力が2重、3重に出力されるのを回避します。
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) 出力が2重、3重に出力されるのを回避します。
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) 出力が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                final 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                final 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                        final 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( final Throwable th) {
204                        kekka = RETURN_NG;
205
206                        final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE )
207                                .append( CR )   // 5.1.5.0 (2010/04/01) 先に改行しておきます。
208                                .append( "データ処理中に例外が発生しました。 [" )
209                                .append( rowNo ).append( "]行目" ).append( CR )
210                                .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                                final HybsProcess process = list.get(i);
216                                errMsg.append( process.toString() );
217                        }
218                        final String errStr = errMsg.toString();
219                        logger.errLog( errStr,th );
220                        LogWriter.log( errStr );
221                }
222                finally {
223                        // 終了 必ず全ての endメソッドをコールします。
224                        logger.logging( "終了処理を行います。" );
225                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
226                        // 5.3.4.0 (2011/04/01) ロガーのreport()を呼びます。(タイトルを追加)
227                        if( param != null ) {
228                                buf.append( logger.report() ).append( CR );
229                                buf.append( param.report() );
230                        }
231
232                        final boolean isOK = kekka == RETURN_OK;
233                        for( int i=0; i<cnt; i++ ) {
234                                final HybsProcess process = list.get(i);
235                                if( process != null ) {
236                                        buf.append( CR ).append( process.report() );
237                                        process.end( isOK );
238                                }
239                        }
240                        // 一番最後に、ParamProcess を終了します。
241                        if( param  != null ) { param.end( isOK ); }             // 5.5.4.5 (2012/07/27) 一連のProcessの end() の最後にします。
242
243                        buf.append( CR );
244                        logger.logging( buf.toString() );
245                        logger.logging( "実行結果は、[" + errCode(kekka) + "] です。" );
246                        final long ed = System.currentTimeMillis();
247                        logger.logging( "合計処理時間 = " + (ed-st) + " (ms) です。" );
248                        logger.logging( new Date( ed ) + " 終了しました。" );
249
250                        // 一番最後に、ParamProcess を終了します。
251                        logger.end( isOK );
252                }
253        }
254
255        /**
256         * 処理の実行結果を返します。
257         *
258         * @return      実行結果
259         * @see #RETURN_INIT
260         */
261        public int getKekka() { return kekka; }
262
263        /**
264         * 処理を行うメインメソッドです。
265         *
266         * @og.rev 4.0.0.0 (2007/11/22) ConnDataFactory の使用を廃止
267         * @og.rev 6.3.1.1 (2015/07/10) LoggerProcessがない場合は、本体で追加しているので、処理を削除します。
268         *
269         * @param       args    コマンド引数配列
270         */
271        public static void main( final String[] args ) {
272                if( args.length == 0 ) {
273                        LogWriter.log( usage() );
274                        return ;
275                }
276
277                // 引数の加工
278                final List<HybsProcess> list = makeHybsProcessList( args );
279
280                // 引数リスト(HybsProcessリスト)を登録
281                final MainProcess process = new MainProcess();
282                process.setList( list );
283
284                // 処理の実行開始
285                process.run();
286        }
287
288        /**
289         * メインに渡された引数配列 より、各 ChainProcess インスタンス を作成します。
290         *
291         * @param       args    メインに渡された引数配列(可変長引数)
292         *
293         * @return      ChainProcessインスタンスのList
294         */
295        private static List<HybsProcess> makeHybsProcessList( final String... args ) {
296                final ArrayList<HybsProcess> list = new ArrayList<>();
297
298                HybsProcess process = null;
299                final Argument argment = new Argument( MainProcess.class.getName() );
300                for( int i=0; i<args.length; i++ ) {
301                        final int type = argment.getArgumentType( args[i] ) ;
302
303                        switch( type ) {
304                                case Argument.CMNT : break;                             // 6.0.2.5 (2014/10/31) break追記
305                                case Argument.ARGS :
306                                        process = (HybsProcess)StringUtil.newInstance( args[i] );
307                                        list.add( process );
308                                        break;
309                                case Argument.PROP :
310                                        if( process != null ) {
311                                                process.putArgument( args[i] );
312                                        }
313                                        break;
314                                default: break;
315                        }
316                }
317                return list;
318        }
319
320        /**
321         * エラーコードに対するメッセージを返します。
322         *
323         * @param       code    エラーコード
324         *
325         * @return      エラーコードに対するメッセージ
326         */
327        public String errCode( final int code ) {
328                final String errMsg ;
329                switch( code ) {
330                        case RETURN_INIT : errMsg = "初期化" ; break;
331                        case RETURN_OK   : errMsg = "正常" ; break;
332                        case RETURN_WARN : errMsg = "警告" ; break;
333                        case RETURN_NG   : errMsg = "異常" ; break;
334                        default :errMsg = "未定義エラー" ; break;
335                }
336                return errMsg ;
337        }
338
339        /**
340         * このクラスの使用方法を返します。
341         *
342         * @return      このクラスの使用方法
343         * @og.rtnNotNull
344         */
345        private static String usage() {
346
347                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
348                        .append( "ChainProcess を実装した各クラスを、順次実行します。" ).append( CR )
349                        .append( "キーと値の間には、スベースを入れないで下さい。").append( CR ).append( CR )
350                        .append( "Usage: java org.opengion.fukurou.process.MainProcess サブChainProcessクラス [[-キー=値] ・・・] [・・・]  " ).append( CR )
351                        .append( "   サブChainProcessクラス :ChainProcess を実装したクラス" ).append( CR )
352                        .append( "     -キー=値             :各サブクラス毎の引数。 - で始まり、= で分割します。" ).append( CR )
353                        .append( "     -AAA=BBB             :複数指定できます。" ).append( CR )
354                        .append( "   サブChainProcessクラス :複数指定できます。" ).append( CR )
355                        .append( "     -CCC=DDD " ).append( CR );
356
357                return buf.toString();
358        }
359}