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