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