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 java.util.MissingResourceException;
019import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
020import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
021import java.util.List;
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.Collections;                                                                           // 6.3.9.0 (2015/11/06)
025import java.util.Objects;                                                                                       // 8.4.2.2 (2023/03/17) ハッシュコード求め
026
027import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
028import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
029import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
030import org.opengion.fukurou.system.DateSet;                                                     // 6.4.2.0 (2016/01/29)
031
032/**
033 * AbstractObjectPool は、生成された Object をプールするキャッシュクラスです。
034 * サブクラスで、各クラスごとにオブジェクトを生成/初期化/終了するように各メソッドを
035 * コーディングしなおしてください。
036 * サブクラスでは、Object createInstance() と、<del>void objectInitial( Object obj )、8.5.4.2 (2024/01/12) </del>
037 * void objectFinal( Object obj )  を オーバーライドしてください。
038 *
039 * ※ 8.5.4.2 (2024/01/12) objectInitial の廃止(実質 使われていなかった)と、objectFinal の abstract 化
040 *
041 * @version  4.0
042 * @author   Kazuhiko Hasegawa
043 * @since    JDK5.0,
044 *
045 * @param <E> 型オブジェクト
046 */
047public abstract class AbstractObjectPool<E> {
048
049        /** 内部でオブジェクトをプールしている配列。 */
050        private List<E>                                                 pool    ;               // プールしているオブジェクト
051        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */
052        private final ConcurrentMap<Integer,TimeStampObject>    poolBkMap = new ConcurrentHashMap<>();          // 作成したオブジェクトのタイムスタンプ管理
053        private final Object lock = new Object();                               // 6.3.9.0 (2015/11/06) ロック用のオブジェクト。poolとpoolBkMapを同時にロックする。
054
055        /** プール自体を拡張可能かどうかを決める変数。拡張制限(true)/無制限(false) */
056        private boolean limit    ;
057
058        /** 最大オブジェクト数 */
059        private int maxsize    ;
060
061        /** 生成したオブジェクトの寿命(秒)を指定します。 0 は、制限なしです。*/
062        private int     limitTime ;             // 3.5.4.3 (2004/01/05) キャッシュの寿命を指定します。
063
064        /** 制限なしの場合でも、実質この値以上のキャッシュは、許可しません。*/
065        private static final int MAX_LIMIT_COUNT = 1000 ;               // 3.6.0.8 (2004/11/19)
066
067        /**
068         * デフォルトコンストラクター
069         *
070         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
071         */
072        protected AbstractObjectPool() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
073
074        /**
075         * 初期化メソッド
076         *
077         * 初期オブジェクト数、最大オブジェクト数、拡張制限を指定します。
078         *
079         * 初期オブジェクト数は、プールを作成すると同時に確保するオブジェクトの個数です。
080         * オブジェクトの生成に時間がかかり、かつ、必ず複数使用するのであれば、
081         * 予め複数確保しておけば、パフォーマンスが向上します。
082         * 最大オブジェクト数は、拡張制限が、無制限(limit = false )の場合は、
083         * 無視されます。制限ありの場合は、この値を上限に、オブジェクトを増やします。
084         * 拡張制限は、生成するオブジェクト数に制限をかけるかどうかを指定します。
085         * 一般に、コネクション等のリソースを確保する場合は、拡張制限を加えて、
086         * 生成するオブジェクト数を制限します。
087         *
088         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
089         *
090         * @param   minsize 初期オブジェクト数
091         * @param   maxsize 最大オブジェクト数
092         * @param   limit   拡張制限(true)/無制限(false)
093         */
094        protected void init( final int minsize, final int maxsize, final boolean limit ) {
095                init( minsize, maxsize, limit,0 ) ;
096        }
097
098        /**
099         * 初期化メソッド
100         *
101         * 初期オブジェクト数、初期配列数、拡張制限、オブジェクトの寿命を指定します。
102         *
103         * 初期オブジェクト数、初期配列数、拡張制限、までは、{@link  #init( int , int , boolean ) init}
104         * を参照してください。
105         * オブジェクトの寿命は、生成された時間からの経過時間(秒)だけ、キャッシュしておく
106         * 場合に使用します。
107         * 例えば、コネクション等で、長期間のプーリングがリソースを圧迫する場合や、
108         * 接続側自身が、タイマーで切断する場合など、オブジェクトの生存期間を
109         * 指定して管理する必要があります。
110         *
111         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
112         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
113         *
114         * @param   minsize 初期オブジェクト数
115         * @param   maxsize 初期配列数
116         * @param   limit   拡張制限(true)/無制限(false)
117         * @param   limitTime オブジェクトの寿命の時間制限値(秒)
118         * @see     #init( int , int , boolean )
119         */
120        protected void init( final int minsize, final int maxsize,final boolean limit,final int limitTime ) {
121                this.maxsize = maxsize;
122                this.limit     = limit;
123                this.limitTime = limitTime;
124                synchronized( lock ) {
125                        pool = Collections.synchronizedList( new ArrayList<>( maxsize ) );              // 6.3.9.0 (2015/11/06)
126                        poolBkMap.clear();                                                                                                              // 6.4.3.1 (2016/02/12)
127                        for( int i=0; i<minsize; i++ ) {
128                                final E obj = createInstance();
129                                pool.add( obj );
130
131                                // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
132//                              final Integer key = Integer.valueOf( obj.hashCode() );
133                                final Integer key = obj.hashCode();
134                                poolBkMap.put( key,new TimeStampObject( obj,limitTime ) );
135                        }
136                }
137        }
138
139        /**
140         * キャッシュのインスタンスを返します。
141         *
142         * なお、拡張制限をしている場合に、最初に確保した数以上のオブジェクト生成の
143         * 要求があった場合は、 MissingResourceException が throw されます。
144         * また、オブジェクトが寿命を超えている場合は、削除した後、新たに次の
145         * オブジェクトの生成を行います。
146         *
147         * @og.rev 4.0.0.1 (2007/12/03) 生成リミットチェックを厳密に行う。
148         * @og.rev 4.0.0.1 (2007/12/03) 生成リミットエラー時に、タイムアウトをチェックする。
149         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
150         *
151         * @return   キャッシュのインスタンス
152         * @throws MissingResourceException 拡張制限により、新しいインスタンスを生成できない場合
153         */
154//      public E newInstance() throws MissingResourceException {
155        public E newInstance() {                                // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidUncheckedExceptionsInSignatures
156                final E rtnobj ;
157                synchronized( lock ) {
158                        if( pool.isEmpty() ) {
159                                if( limit && poolBkMap.size() >= maxsize ) {
160                                        final String errMsg = "生成リミットいっぱいで新たに生成できません。["
161                                                                + poolBkMap.size() + "]";
162
163                                        // 4.0.0.1 (2007/12/03) 生成リミットエラー時に、タイムアウトをチェックする。
164                                        final Iterator<TimeStampObject> itr = poolBkMap.values().iterator();
165                                        while( itr.hasNext() ) {
166                                                final TimeStampObject tso = itr.next();
167                                                if( tso == null || tso.isTimeOver() ) {
168                                                        itr.remove();
169                                                }
170                                        }
171
172                                        throw new MissingResourceException( errMsg,getClass().getName(),"limit" );
173                                }
174                                else if( poolBkMap.size() > MAX_LIMIT_COUNT ) {
175                                        clear();                // 全件キャッシュを破棄します。
176                                        final String errMsg = "ObjectPool で、メモリリークの可能性があります。size=["
177                                                                + poolBkMap.size() + "]";
178                                        throw new OgRuntimeException( errMsg );
179                                }
180                                // 新規作成
181                                rtnobj = createInstance();
182                                // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
183//                              final Integer key = Integer.valueOf( rtnobj.hashCode() );
184                                final Integer key = rtnobj.hashCode();
185                                poolBkMap.put( key,new TimeStampObject( rtnobj,limitTime ) );
186                        }
187                        else {
188                                // 既存取り出し
189                                rtnobj = pool.remove(0);
190                                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
191                                if( rtnobj == null ) {
192                                        // 通常ありえない。
193                                        final String errMsg = "オブジェクトの取得に失敗しました。" ;
194                                        throw new MissingResourceException( errMsg,getClass().getName(),"pool" );
195                                }
196
197                                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
198//                                      final Integer key = Integer.valueOf( rtnobj.hashCode() );
199                                        final Integer key = rtnobj.hashCode();
200                                        final TimeStampObject tso = poolBkMap.get( key );
201                                        if( tso == null || tso.isTimeOver() ) {
202                                                remove( rtnobj );
203                                                return newInstance();
204                                        }
205                        }
206                }
207                return rtnobj;
208        }
209
210        /**
211         * 具体的に新しいインスタンスを生成するメソッド。
212         *
213         * サブクラスで具体的に記述する必要があります。
214         *
215         * @return   新しいインスタンス
216         */
217        protected abstract E createInstance();
218
219        /**
220         * オブジェクトを、オブジェクトプールに戻します。
221         * 戻すべきオブジェクトが null の場合は、削除されたと判断します。
222         *
223         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
224         * @og.rev 8.5.4.2 (2024/01/12) objectInitial 廃止
225         *
226         * @param   obj2 オブジェクトプールに戻すオブジェクト
227         */
228        public void release( final E obj2 ) {
229//              final E obj2 = objectInitial( obj );
230                if( obj2 != null ) {
231                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
232//                      final Integer key = Integer.valueOf( obj2.hashCode() );
233                        final Integer key = obj2.hashCode();
234                        synchronized( lock ) {
235                                final TimeStampObject tso = poolBkMap.get( key );
236                                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
237                                if( tso == null ) {
238                                        // 6.0.2.5 (2014/10/31) Ctrl-C で終了させると、なぜか poolBkMap から、オブジェクトが消える。原因不明????
239                                        //                      LogWriter.log( "ObjectPool で、メモリリークの可能性がある。obj=[" + obj + "]" );
240                                        remove( obj2 );
241                                }
242                                else {
243                                        pool.add( obj2 );
244                                }
245                        }
246                }
247        }
248
249        /**
250         * オブジェクトを、オブジェクトプールから削除します。
251         * remove されるオブジェクトは、すでにキャッシュから取り出された後なので、
252         * そのまま、何もしなければ自然消滅(GC)されます。
253         * 自然消滅する前に、objectFinal( Object ) が呼ばれます。
254         * 生成されたオブジェクトの総数も、ひとつ減らします。
255         *
256         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
257         *
258         * @param   obj 削除するオブジェクト
259         */
260        public void remove( final E obj ) {
261                if( obj != null ) {
262                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
263//                      final Integer key = Integer.valueOf( obj.hashCode() );
264                        final Integer key = obj.hashCode();
265                        synchronized( lock ) {
266                                poolBkMap.remove( key );
267                        }
268                }
269
270                objectFinal( obj );
271        }
272
273        /**
274         * オブジェクトプールの要素数を返します。
275         *
276         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
277         *
278         * @return   プールの要素数
279         */
280        public int size() {
281                synchronized( lock ) {
282                        return poolBkMap.size();
283                }
284        }
285
286        /**
287         * オブジェクトプールが要素を持たないかどうかを判定します。
288         *
289         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
290         *
291         * @return   オブジェクトプールが要素を持っていない、つまりそのサイズが 0 の場合にだけ true、そうでない場合は false
292         */
293        public boolean isEmpty() {
294                synchronized( lock ) {
295                        return poolBkMap.isEmpty() ;
296                }
297        }
298
299        /**
300         * すべての要素を オブジェクトプールから削除します。
301         * 貸し出し中のオブジェクトは、クリアしません。よって、返り値は、
302         * すべてのオブジェクトをクリアできた場合は、true、貸し出し中の
303         * オブジェクトが存在した場合(クリアできなかった場合)は、false です。
304         *
305         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
306         *
307         * @return すべてクリア(true)/貸し出し中のオブジェクトが残っている(false)
308         */
309        public boolean clear() {
310                synchronized( lock ) {
311                        final Iterator<E> itr = pool.iterator();
312                        while( itr.hasNext() ) {
313                                remove( itr.next() );
314                        }
315                        pool.clear();
316
317                        // 貸し出し中の場合は、remove 出来ない為、poolBkMap に残っている。
318                        // それでも、poolBkMap をクリアすることで、release 返却時にも、
319                        // remove されるようになります。
320                        // ただし、作成オブジェクト数が、一旦 0 にリセットされる為、
321                        // 最大貸し出し可能数が、一時的に増えてしまいます。
322                        final boolean flag = poolBkMap.isEmpty();
323                        poolBkMap.clear();
324
325                        return flag;
326                }
327        }
328
329        /**
330         * オブジェクトプールから削除するときに呼ばれます。
331         * このメソッドで各オブジェクトごとの終了処理を行います。
332         * 例えば、データベースコネクションであれば、close() 処理などです。
333         *
334         * デフォルトでは、なにも行いません。
335         *
336         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
337         * @og.rev 8.5.4.2 (2024/01/12) abstract メソッド化
338         *
339         * @param  obj 終了処理を行うオブジェクト
340         */
341        protected abstract void objectFinal( final E obj );
342//      protected void objectFinal( final E obj ) {
343//              // ここでは処理を行いません。
344//      }
345
346//      /**
347//       * オブジェクトプールに戻すとき(release するとき)に呼ばれます。
348//       * このメソッドで各オブジェクトごとの初期処理を行います。
349//       * オブジェクトプールに戻すときには、初期化して、次の貸し出しに
350//       * 対応できるように、初期処理しておく必要があります。
351//       *
352//       * デフォルトでは、引数のオブジェクトをそのまま返します。
353//       *
354//       * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
355//       * @og.rev 8.5.4.2 (2024/01/12) objectInitial 廃止
356//       *
357//       * @param  obj 初期処理を行うオブジェクト
358//       *
359//       * @return 初期処理を行ったオブジェクト
360//       */
361//      protected E objectInitial( final E obj ) {
362//              return obj;
363//      }
364
365        /**
366         * 内部状況を簡易的に表現した文字列を返します。
367         *
368         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
369         *
370         * @return   このオブジェクトプールの文字列表現
371         * @og.rtnNotNull
372         */
373        @Override               // Object
374        public String toString() {
375                synchronized( lock ) {
376                        // 6.0.2.5 (2014/10/31) char を append する。
377                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveLiteralAppends 対応
378                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
379                                .append( "  freeCount   = [" ).append( pool.size()              ).append( ']'   ).append( CR )
380                                .append( "  createCount = [" ).append( poolBkMap.size() )
381                                .append( "] ( max=["             ).append( maxsize                      ).append( "] )" ).append( CR )
382                                .append( "  limiter     = [" ).append( limit                    ).append( ']'   ).append( CR )
383                                .append( "  limitTime   = [" ).append( limitTime                ).append( "](s)" ).append( CR );
384
385                        final Iterator<E> itr = pool.iterator();
386                        buf.append( "Free Objects " ).append( CR );
387                        while( itr.hasNext() ) {
388                                final E obj = itr.next();
389                                if( obj != null ) {
390                                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
391//                                      final Integer key = Integer.valueOf( obj.hashCode() );
392                                        final Integer key = obj.hashCode();
393                                        buf.append( ' ' ).append( poolBkMap.get( key ) )
394                                                .append( ' ' ).append( obj ).append( CR );
395                                }
396                        }
397                        return buf.toString();
398                }
399        }
400}
401
402/**
403 * TimeStampObject は、生成された Object を、生成時刻とともに管理するクラスです。
404 * 内部のハッシュキーは、登録するオブジェクトと同一で、管理できるのは、異なるオブジェクト
405 * のみです。
406 *
407 * @version  4.0
408 * @author   Kazuhiko Hasegawa
409 * @since    JDK5.0,
410 */
411// 8.5.5.1 (2024/02/29) spotbugs CT_CONSTRUCTOR_THROW(コンストラクタで、Excweptionを出さない) class を final にすれば、警告は消える。
412// class TimeStampObject implements Comparable<TimeStampObject> {       // 4.3.3.6 (2008/11/15) Generics警告対応
413final class TimeStampObject implements Comparable<TimeStampObject> {    // 4.3.3.6 (2008/11/15) Generics警告対応
414        private final long              timeStamp ;
415        private final long              limitTime ;
416        private final String    objStr ;                        // 6.0.2.5 (2014/10/31) 表示用
417        private final int               hcode ;
418
419        /**
420         * コンストラクター。
421         *
422         * @og.rev 8.4.2.2 (2023/03/17) ハッシュコード求めに、java.util.Objects#hash を使用します。
423         *
424         * @param  obj 管理するオブジェクト
425         * @param  limit オブジェクトの寿命(秒)
426         * @throws IllegalArgumentException TimeStampObject のインスタンスに、NULL はセットできません。
427         */
428        public TimeStampObject( final Object obj,final int limit ) {
429                if( obj == null ) {
430                        final String errMsg = "TimeStampObject のインスタンスに、NULL はセットできません。" ;
431                        throw new IllegalArgumentException( errMsg );
432                }
433                objStr = String.valueOf( obj );         // 6.0.2.5 (2014/10/31) 表示用
434
435                timeStamp = System.currentTimeMillis();
436                if( limit > 0 ) {
437                        limitTime = timeStamp + limit * 1000L ;
438                }
439                else {
440                        limitTime = Long.MAX_VALUE ;
441                }
442
443                // 8.4.2.2 (2023/03/17) ハッシュコード求めに、java.util.Objects#hash を使用します。
444//              hcode = (int)((timeStamp)&(Integer.MAX_VALUE))^(obj.hashCode()) ;
445                // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
446//              hcode = Objects.hash( objStr, Long.valueOf( timeStamp ) );
447                hcode = Objects.hash( objStr, timeStamp );
448        }
449
450        /**
451         * 内部管理しているオブジェクトの生成時刻を返します。
452         *
453         * @return   生成時刻(ms)
454         */
455        public long getTimeStamp() {
456                return timeStamp;
457        }
458
459        /**
460         * オブジェクトの寿命がきたかどうかを返します。
461         *
462         * @return   寿命判定(true:寿命/false:まだ使える)
463         */
464        public boolean isTimeOver() {
465                return System.currentTimeMillis() > limitTime ;
466        }
467
468        /**
469         * オブジェクトが同じかどうかを判定します。
470         *
471         * 内部オブジェクトの equals() メソッドと、作成時刻の両方を判断します。
472         * 内部オブジェクトの equals() が同じでも、作成時刻が異なると、
473         * false を返します。これは、全く同一オブジェクトを管理する場合でも、
474         * タイムスタンプを差し替える事で、異なるオブジェクトとして
475         * 認識させるということです。
476         *
477         * @og.rev 8.4.2.2 (2023/03/17) ハッシュコード求めに対応した、equals に変更します。
478         *
479         * @param    obj オブジェクト
480         *
481         * @return   true:同じ/false:異なる。
482         */
483        @Override               // Object
484        public boolean equals( final Object obj ) {
485                if( obj instanceof TimeStampObject ) {
486                        final TimeStampObject other = (TimeStampObject)obj ;
487//                      return hcode == other.hcode && timeStamp == other.timeStamp ;
488                        return objStr.equals( other.objStr ) && timeStamp == other.timeStamp ;
489                }
490                return false ;
491        }
492
493        /**
494         * ハッシュコードを返します。
495         *
496         * ここで返すのは、自分自身のハッシュコードではなく、
497         * 内部管理のオブジェクトのハッシュコードです。
498         *
499         * <del> hashcode = (int)((timeStamp)&amp;(Integer.MAX_VALUE))^(obj.hashCode()) </del>
500         * hcode = Objects.hash( objStr, Long.valueOf( timeStamp ) );
501         *
502         * この計算式は、変更される可能性があります。
503         *
504         * @return  内部管理のオブジェクトのハッシュコード
505         */
506        @Override               // Object
507        public int hashCode() { return hcode; }
508
509        /**
510         * このオブジェクトと指定されたオブジェクトの順序を比較します。
511         *
512         * このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、
513         * 等しい場合はゼロ、大きい場合は正の整数を返します。
514         *
515         * @param  other TimeStampObject オブジェクト
516         *
517         * @return  順序比較の値
518         * @throws ClassCastException 指定されたオブジェクトがキャストできない場合。
519         * @see Comparable#compareTo(Object)
520         */
521        @Override       // Comparable
522        public int compareTo( final TimeStampObject other ) {   // 4.3.3.6 (2008/11/15) Generics警告対応
523                // 8.5.5.1 (2024/02/29) 比較方法を変更します。
524                final int diff = Long.compare( timeStamp,other.timeStamp );
525                return diff == 0
526                                ? ( objStr.equals( other.objStr ) ? 0 : hcode - other.hcode )
527                                : diff ;
528
529//              final long diff = timeStamp - other.timeStamp;
530//
531//              if( diff > 0 ) { return 1; }
532//              else if( diff < 0 ) { return -1; }
533//              else {
534//                      if( equals( other ) ) { return 0; }
535//                      else { return hcode - other.hcode; }
536//              }
537        }
538
539        /**
540         * このオブジェクトの内部表現を返します。
541         *
542         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
543         *
544         * @return  オブジェクトの内部表現文字列
545         * @og.rtnNotNull
546         */
547        @Override               // Object
548        public String toString() {
549                // Create Timeは、一度求めれば変わらないので、キャッシュしても良い。
550                return "[CreateTime=" + DateSet.getDate( timeStamp,"yyyy/MM/dd HH:mm:ss" )
551                         + " , TimeOver=" + (int)((limitTime - System.currentTimeMillis())/1000.0) + "(s)"
552                         + " , object=" + objStr + "]" ;                // 6.0.2.5 (2014/10/31) 表示用
553        }
554}