    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.util;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import java.util.TimerTask;
    020import java.util.Date;
    021import java.util.Map;
    022import java.util.concurrent.ConcurrentMap;                                      // 6.4.3.3 (2016/03/04)
    023import java.util.concurrent.ConcurrentHashMap;                          // 6.4.3.3 (2016/03/04)
    024import java.util.concurrent.atomic.AtomicInteger;                       // 5.5.2.6 (2012/05/25) findbugs対応
    025
    026import org.opengion.fukurou.system.LogWriter;                           // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
    027import org.opengion.fukurou.system.DateSet;                                     // 6.4.2.0 (2016/01/29)
    028
    029/**
    030 * HybsTimerTask.java は、String 型キーにString型値を Map するクラスです。
    031 *
    032 * HTMLのPOST/GET等の受け渡しや、String型の引数が多い場合に効果があります。
    033 * 特に、getHybsTimerTask( String[] param ) による属性リスト作成は、
    034 * HTMLタグの属性定義を行う上で、非常に便利に利用できます。
    035 *
    036 * この実装は同期化されません。
    037 *
    038 * @version  4.0
    039 * @author   Kazuhiko Hasegawa
    040 * @since    JDK5.0,
    041 */
    042public abstract class HybsTimerTask extends TimerTask implements Comparable<HybsTimerTask> {    // 4.3.3.6 (2008/11/15) Generics警告対応
    043
    044        private static AtomicInteger counter = new AtomicInteger();             // 5.5.2.6 (2012/05/25) findbugs対応
    045
    046        private final int       uniqKey ;                               // タイマータスクのユニークキー
    047        private final long      createTime      = System.currentTimeMillis() ;  // オブジェクトが生成された時刻
    048        // 3.2.2.0 (2003/05/31) HybsTimerTask に対して、設定値を渡せるように変更。
    049        /** 6.4.3.3 (2016/03/04) ConcurrentHashMap に置き換えます。 */
    050        private final ConcurrentMap<String,String>      paramMap = new ConcurrentHashMap<>();   // タイマータスクのパラメータ 3.6.0.7 (2004/11/06)
    051
    052        private String          name            ;                       // タイマータスクの名称
    053        private String          comment         ;                       // タイマータスクの説明
    054        private String          body            ;                       // タイマータスクのボディー要素
    055        private String          startTime       = "000000";     // 24時間制の開始時刻
    056        private String          stopTime        = "000000";     // 24時間制の終了時刻
    057        private int                     startStop       ;                       // 24時間制の日付またがりをチェック。
    058        private boolean         aliveFlag       = true ;        // 活きているかどうか
    059        private int                     errorSleep      ;                       // TimerTask がエラーのときのスリープ時間(s) 3.7.0.4 (2005/03/14)
    060
    061        /**
    062         * デフォルトコンストラクター
    063         * オブジェクトは、newInstance でのみ、生成されます。
    064         *
    065         * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。staticフィールドへの書き込みに、AtomicInteger を利用します。
    066         * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
    067         */
    068        public HybsTimerTask() {
    069                super();
    070                // 4.3.4.4 (2009/01/01)
    071                uniqKey = counter.getAndIncrement() ;   // 5.5.2.6 (2012/05/25) findbugs対応
    072        }
    073
    074        /**
    075         * このタイマータスクによって実行されるアクションです。
    076         * ここでは、エラートラップを入れていますので、サブクラスで
    077         * 再定義できないように、final 化しています。
    078         * サブクラスでは、stratDaemon() をオーバーライドしてください。
    079         *
    080         * @see java.util.TimerTask#run()
    081         * @see #startDaemon()
    082         */
    083        @Override
    084        public final void run() {
    085                try {
    086                        if( isExecution() ) {
    087                                startDaemon();
    088                        }
    089                }
    090                catch( final Throwable th) {
    091                        // 3.7.0.4 (2005/03/14)
    092                        if( errorSleep > 0 ) {
    093                                try { Thread.sleep( errorSleep * 1000L ); }
    094                                catch( final InterruptedException ex ) { LogWriter.log( ex ); }
    095                                System.out.println( th.getMessage() );
    096                        }
    097                        else {
    098                                cancel();
    099                                throw new OgRuntimeException( th );
    100                        }
    101                }
    102        }
    103
    104        /**
    105         * このタイマータスクによって実行されるアクションです。
    106         * run メソッドより呼ばれます。
    107         * サブクラスでは、startDaemon() をオーバーライドしてください。
    108         *
    109         * @see #run()
    110         */
    111        protected abstract void startDaemon() ;
    112
    113        /**
    114         * このタイマータスクによって初期化されるアクションです。
    115         * サブクラスでは、initDaemon() をオーバーライドしてください。
    116         *
    117         */
    118        public void initDaemon() {
    119                // ここでは処理を行いません。
    120        }
    121
    122        /**
    123         * タイマータスクの名称(ユニークキー)を設定します。
    124         *
    125         * @param   nm タイマータスクの名称
    126         */
    127        public void setName( final String nm ) {
    128                name = nm;
    129        }
    130
    131        /**
    132         * タイマータスクの名称(ユニークキー)を取得します。
    133         *
    134         * @return   タイマータスクの名称
    135         */
    136        public String getName() {
    137                return name;
    138        }
    139
    140        /**
    141         * タイマータスクの説明を設定します。
    142         *
    143         * @param   cmt タイマータスクの説明
    144         */
    145        public void setComment( final String cmt ) {
    146                comment = cmt;
    147        }
    148
    149        /**
    150         * タイマータスクの説明を取得します。
    151         *
    152         * @return   タイマータスクの説明
    153         */
    154        public String getComment() {
    155                return comment;
    156        }
    157
    158        /**
    159         * このオブジェクトの内部ユニークキー値を返します。
    160         * オブジェクト生成毎に、＋１ されて、内部に持っています。
    161         *
    162         * @return   オブジェクトの内部ユニークキー
    163         */
    164        public int getUniqKey() {
    165                return uniqKey;
    166        }
    167
    168        /**
    169         * このオブジェクトが生成された時刻をミリ秒で返します。
    170         * オブジェクト生成時に、System.currentTimeMillis() の値を取得しています。
    171         *
    172         * @return   オブジェクトが生成された時刻(ミリ秒)
    173         */
    174        public long getCreateTime() {
    175                return createTime;
    176        }
    177
    178        /**
    179         * 内部で使用するパラメータを設定します。
    180         *
    181         * 外部より、引数として渡されてきます。これを利用して、各サブシステムは、
    182         * パラメーターを設定したり、初期化したり利用出来ます。
    183         *
    184         * ※ 6.4.3.1 (2016/02/12) で、内部のMapを ConcurrentHashMap に置き換えたが、引数のMapが
    185         *    not null制限が入っているかどうか不明なので、取りえず元に戻しておきます。
    186         * @og.rev 6.4.3.3 (2016/03/04) 受け取って、Mapに登録するときに、not null制限チェックを入れます。
    187         *
    188         * @param       map     パラメータマップ
    189         */
    190        public void setParameter( final Map<String,String> map ) {
    191                if( map != null ) {
    192                        // ※ 通常の HashMap から、ConcurrentHashMap への値の設定なので、null チェックが必要です。
    193                        map.forEach( (k,v) -> { if( k != null && v != null ) { paramMap.put( k,v ); } } );
    194                }
    195        }
    196
    197        /**
    198         * 内部で使用するパラメータを返します。
    199         *
    200         * Mapオブジェクトそのものですので、サブクラス等で書き換えた場合、内容も書き換わってしまいます。
    201         *
    202         * @og.rev 6.9.0.0 (2018/01/31) 新規追加
    203         *
    204         * @return      内部で使用するパラメータMap
    205         */
    206        protected ConcurrentMap<String,String> getParameter() {
    207                return paramMap;
    208        }
    209
    210        /**
    211         * 内部で使用するパラメータのキーに対する値を取得します。
    212         *
    213         * 各サブシステムは、パラメーターを設定したり、初期化したり利用出来ます。
    214         *
    215         * @param       key 引数のキー
    216         *
    217         * @return      キーに対する値
    218         */
    219        public String getValue( final String key ) {
    220                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    221                // 反転注意
    222                return key == null ? null : paramMap.get( key ) ;                                       // 6.4.3.3 (2016/03/04)
    223        }
    224
    225        /**
    226         * 内部で使用するBody要素の値を設定します。
    227         *
    228         * 外部より、引数として渡されてきます。これを利用して、各サブシステムは、
    229         * パラメーターを設定したり、初期化したり利用出来ます。
    230         *
    231         * @param       body Body要素の値
    232         */
    233        public void setBody( final String body ) {
    234                this.body = body ;
    235        }
    236
    237        /**
    238         * 内部で使用するBody要素の値を取得します。
    239         *
    240         * 各サブシステムは、パラメーターを設定したり、初期化したり利用出来ます。
    241         *
    242         * @return      Body要素の値
    243         */
    244        public String getBody() {
    245                return body ;
    246        }
    247
    248        /**
    249         * 24時間制(YYMMDD)の開始時刻を設定します。
    250         *
    251         * 指定時刻範囲内での実行のみ許可するように開始時刻を設定します。
    252         * これは、タイマーで指定した間隔ごとにチェックを入れるので、チェック時間が
    253         * 長い場合は、正確に開始時刻から始まるというものではありません。
    254         * 初期値は、"000000" です。
    255         *
    256         * @param       st 開始時刻
    257         */
    258        public void setStartTime( final String st ) {
    259                if( st == null || st.length() != 6 ) {
    260                        final String errMsg = "startTime is inaccurate." +
    261                                                        "startTime=[" + st + "]" ;
    262                        throw new OgRuntimeException( errMsg );
    263                }
    264                startTime = st ;
    265                initStartStop();
    266        }
    267
    268        /**
    269         * 24時間制(YYMMDD)の終了時刻を設定します。
    270         *
    271         * 指定時刻範囲内での実行のみ許可するように終了時刻を設定します。
    272         * これは、タイマーで指定した間隔ごとにチェックを入れるので、チェック時間が
    273         * 長い場合は、正確に終了時刻で終了するというものではありません。
    274         * (終了時刻を越えてからの新規実行はありません。)
    275         * 初期値は、"000000" です。
    276         *
    277         * @param       st 終了時刻
    278         */
    279        public void setStopTime( final String st ) {
    280                if( st == null || st.length() != 6 ) {
    281                        final String errMsg = "stopTime is inaccurate." +
    282                                                        "stopTime=[" + st + "]" ;
    283                        throw new OgRuntimeException( errMsg );
    284                }
    285                stopTime = st ;
    286                initStartStop();
    287        }
    288
    289        /**
    290         * TimerTask がエラー発生時のスリープ時間(s) 設定します(初期値:0)。
    291         *
    292         * これは、予期せぬエラー(たとえば、データベースが落ちていたなど)が
    293         * 発生したときでも、TimerTask を終了させずに、Sleep させて待機させる
    294         * 事により、原因が除去された場合に、自動復帰するようにします。
    295         * これに、０ を設定すると、エラー時に即 終了します。
    296         * 設定は、秒で指定してください。
    297         *
    298         * @param       erTime スリープ時間(s)(初期値:0)
    299         */
    300        public void setErrorSleepSec( final int erTime ) {
    301                errorSleep = erTime ;
    302        }
    303
    304        /**
    305         * 24時間制の開始/終了時刻の日付またがりを初期設定します。
    306         *
    307         * 開始時刻、終了時刻は、２４時間制でしか指定できません。(日付指定できない)
    308         * そのため、朝７：００から夜２２：００などの 開始＜終了 の時は単純な範囲チェックで
    309         * 判断できますが、夜２２：００から朝７：００に実行したい場合は、異なるチェックが
    310         * 必要になります。
    311         * また、開始時刻と終了時刻が未設定の場合や、同じ時刻の場合は、常に実行する必要が
    312         * あります。これらのチェックを毎回行うのではなく、開始/終了時刻設定時にチェックして
    313         * おきます。
    314         *
    315         */
    316        private void initStartStop() {
    317                if( startTime == null || stopTime == null ) {
    318                        startStop = 0;
    319                }
    320                else {
    321                        startStop = startTime.compareTo( stopTime );
    322                }
    323        }
    324
    325        /**
    326         * 実行可能時間内かどうかを判定します。
    327         *
    328         * 設定された開始時刻と終了時刻に基づいて、現時刻で実行可能かどうか
    329         * 判断します。
    330         *
    331         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
    332         *
    333         * @return      (true:実行許可  false:停止)
    334         */
    335        private boolean isExecution() {
    336                boolean rtnFlag = false;
    337                final String time = DateSet.getDate( "HHmmss" );                // 5.5.7.2 (2012/10/09) HybsDateUtil を利用
    338
    339                if( startStop == 0 ) {
    340                        rtnFlag = true;
    341                }
    342                else if( startStop < 0 ) {
    343                        if( startTime.compareTo( time ) < 0 &&
    344                                time.compareTo( stopTime )  < 0 ) {
    345                                        rtnFlag = true;
    346                        }
    347                }
    348                else {                                                                                                          // 6.3.9.0 (2015/11/06) 条件は効果がない（findbugs）
    349                        if( startTime.compareTo( time ) < 0 ||
    350                                time.compareTo( stopTime )  < 0 ) {
    351                                        rtnFlag = true;
    352                        }
    353                }
    354                return rtnFlag;
    355        }
    356
    357        /**
    358         * このオブジェクトと指定されたオブジェクトの順序を比較します。
    359         * このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、
    360         * 等しい場合はゼロ、大きい場合は正の整数を返します。
    361         *
    362         * @param    other 比較対象の Object
    363         *
    364         * @return   このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
    365         * @throws ClassCastException 指定されたオブジェクトの型が原因で、この Object と比較できない場合
    366         */
    367        @Override       // Comparable
    368        public int compareTo( final HybsTimerTask other ) {             // 4.3.3.6 (2008/11/15) Generics警告対応
    369
    370                if( name == null && other.name != null ) { return -1; }
    371                if( name != null && other.name == null ) { return  1; }
    372
    373                if( name != null && other.name != null ) {
    374                        final int nmComp = name.compareTo( other.name );
    375                        if( nmComp != 0 ) { return nmComp; }
    376                }
    377
    378                return uniqKey - other.uniqKey ;
    379        }
    380
    381        /**
    382         * このオブジェクトの文字列表現を返します。
    383         * 基本的にデバッグ目的に使用します。
    384         *
    385         * @return このクラスの文字列表現
    386         * @og.rtnNotNull
    387         */
    388        @Override
    389        public String toString() {
    390                return  name + " [" + uniqKey + "]";
    391        }
    392
    393        /**
    394         * このオブジェクトと他のオブジェクトが等しいかどうかを示します。
    395         *
    396         * @og.rev 8.4.2.2 (2023/03/17) instanceof を使用した比較方法に統一します。
    397         *
    398         * @param  object 比較対象の参照オブジェクト
    399         *
    400         * @return      引数に指定されたオブジェクトとこのオブジェクトが等しい場合は true、そうでない場合は false
    401         */
    402        @Override
    403        public boolean equals( final Object object ) {
    404                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    405                // 条件反転注意
    406//              return object != null
    407//                              && getClass().equals( object.getClass() )
    408//                              && super.equals( object )
    409//                              && uniqKey == ((HybsTimerTask)object).uniqKey ;
    410
    411                // 8.4.2.2 (2023/03/17) instanceof を使用した比較方法に統一します。
    412//              return super.equals( object )                                                           // 6.9.7.0 (2018/05/14) object != null が保障されます。
    413//                              && getClass().equals( object.getClass() )
    414//                              && uniqKey == ((HybsTimerTask)object).uniqKey ;
    415
    416                return object instanceof HybsTimerTask
    417                                && uniqKey == ((HybsTimerTask)object).uniqKey ;
    418        }
    419
    420        /**
    421         * オブジェクトが生存しているかどうかを判定します。
    422         *
    423         * @return 生存しているかどうか。true:生存 / false:キャンセル済み
    424         */
    425        public boolean isAlive() {
    426                return aliveFlag ;
    427        }
    428
    429        /**
    430         * オブジェクトのハッシュコード値を返します。
    431         *
    432         * @return このオブジェクトのハッシュコード値
    433         */
    434        @Override
    435        public int hashCode() {
    436                return uniqKey ;
    437        }
    438
    439        /**
    440         * このタイマータスクのcancel() メソッドをオーバーライドします。
    441         *
    442         * @return キャンセルが正常に終了できたかどうか
    443         * @see java.util.TimerTask#cancel()
    444         */
    445        @Override
    446        public boolean cancel() {
    447                if( aliveFlag ) {
    448                        System.out.println();
    449                        System.out.println( toString() + " " + new Date()  + " Stoped" );
    450                        aliveFlag = false;              // cancelTask を呼ぶ前に必ず『false』にしておく。
    451                }
    452                return super.cancel();
    453        }
    454}