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