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