1 19 package org.apache.avalon.excalibur.pool; 20 21 import java.util.Iterator ; 22 import java.util.LinkedList ; 23 24 import org.apache.avalon.framework.activity.Disposable; 25 import org.apache.avalon.framework.logger.AbstractLogEnabled; 26 import org.apache.avalon.framework.logger.LogEnabled; 27 import org.apache.avalon.framework.thread.ThreadSafe; 28 29 import org.apache.excalibur.instrument.CounterInstrument; 30 import org.apache.excalibur.instrument.Instrument; 31 import org.apache.excalibur.instrument.Instrumentable; 32 import org.apache.excalibur.instrument.ValueInstrument; 33 34 49 public class InstrumentedResourceLimitingPool 50 extends AbstractLogEnabled 51 implements Pool, LogEnabled, Disposable, ThreadSafe, Instrumentable 52 { 53 public static final String DEFAULT_INSTRUMENTABLE_NAME = "pool"; 54 public static final String INSTRUMENT_SIZE_NAME = "size"; 55 public static final String INSTRUMENT_READY_SIZE_NAME = "ready-size"; 56 public static final String INSTRUMENT_GETS_NAME = "gets"; 57 public static final String INSTRUMENT_PUTS_NAME = "puts"; 58 public static final String INSTRUMENT_BLOCKS_NAME = "blocks"; 59 public static final String INSTRUMENT_CREATES_NAME = "creates"; 60 public static final String INSTRUMENT_DECOMMISSIONS_NAME = "decommissions"; 61 62 65 68 protected final Object m_semaphore = new Object (); 69 70 73 76 private boolean m_disposed = false; 77 78 81 private final ObjectFactory m_factory; 82 83 86 private final int m_max; 87 88 91 private final boolean m_maxStrict; 92 93 97 private final boolean m_blocking; 98 99 103 private final long m_blockTimeout; 104 105 108 private final long m_trimInterval; 109 110 113 private long m_lastTrim; 114 115 118 private LinkedList m_ready; 119 120 123 private int m_readySize; 124 125 128 private LinkedList m_oldReady; 129 130 133 private int m_oldReadySize; 134 135 138 private int m_size; 139 140 141 private String m_instrumentableName = DEFAULT_INSTRUMENTABLE_NAME; 142 143 144 private ValueInstrument m_sizeInstrument; 145 146 147 private ValueInstrument m_readySizeInstrument; 148 149 150 private CounterInstrument m_getsInstrument; 151 152 153 private CounterInstrument m_putsInstrument; 154 155 156 private CounterInstrument m_blocksInstrument; 157 158 159 private CounterInstrument m_createsInstrument; 160 161 162 private CounterInstrument m_decommissionsInstrument; 163 164 167 183 public InstrumentedResourceLimitingPool( final ObjectFactory factory, 184 int max, 185 boolean maxStrict, 186 boolean blocking, 187 long blockTimeout, 188 long trimInterval ) 189 { 190 m_factory = factory; 191 m_max = ( max <= 0 ? Integer.MAX_VALUE : max ); 192 m_maxStrict = maxStrict; 193 m_blocking = blocking; 194 m_blockTimeout = blockTimeout; 195 m_trimInterval = trimInterval; 196 197 m_ready = new LinkedList (); 199 if( m_trimInterval > 0 ) 200 { 201 m_oldReady = new LinkedList (); 202 } 203 204 m_sizeInstrument = new ValueInstrument( INSTRUMENT_SIZE_NAME ); 206 m_readySizeInstrument = new ValueInstrument( INSTRUMENT_READY_SIZE_NAME ); 207 m_getsInstrument = new CounterInstrument( INSTRUMENT_GETS_NAME ); 208 m_putsInstrument = new CounterInstrument( INSTRUMENT_PUTS_NAME ); 209 m_blocksInstrument = new CounterInstrument( INSTRUMENT_BLOCKS_NAME ); 210 m_createsInstrument = new CounterInstrument( INSTRUMENT_CREATES_NAME ); 211 m_decommissionsInstrument = new CounterInstrument( INSTRUMENT_DECOMMISSIONS_NAME ); 212 } 213 214 217 227 public Poolable get() throws Exception 228 { 229 if( m_disposed ) throw new IllegalStateException ( "Already Disposed" ); 230 231 Poolable poolable; 232 int readySize; 233 synchronized( m_semaphore ) 234 { 235 if( ( m_oldReady != null ) && 237 ( System.currentTimeMillis() - m_lastTrim >= m_trimInterval ) ) 238 { 239 trimInner(); 240 } 241 242 if( m_readySize > 0 ) 244 { 245 poolable = (Poolable)m_ready.removeLast(); 247 m_readySize--; 248 } 249 else if( m_oldReadySize > 0 ) 250 { 251 poolable = (Poolable)m_oldReady.removeLast(); 253 m_oldReadySize--; 254 } 255 else 256 { 257 if( ( m_size >= m_max ) && m_maxStrict ) 259 { 260 263 if( m_blocking ) 265 { 266 long blockStart = System.currentTimeMillis(); 267 268 if( getLogger().isDebugEnabled() ) 269 { 270 getLogger().debug( "Blocking until a Poolable is available. " 271 + "Thread: " + Thread.currentThread().getName() ); 272 } 273 274 m_blocksInstrument.increment(); 276 277 if( m_blockTimeout > 0 ) 278 { 279 long blockWait = m_blockTimeout; 284 do 285 { 286 if( blockWait > 0 ) 287 { 288 try 289 { 290 m_semaphore.wait( blockWait ); 291 } 292 catch( InterruptedException e ) 293 { 294 } 295 296 if( m_disposed ) 298 { 299 throw new IllegalStateException ( "Already Disposed" ); 300 } 301 302 if( m_readySize == 0 ) 303 { 304 long now = System.currentTimeMillis(); 306 blockWait = m_blockTimeout - ( now - blockStart ); 307 } 308 } 309 else 310 { 311 long now = System.currentTimeMillis(); 313 314 if( getLogger().isDebugEnabled() ) 315 { 316 getLogger().debug( 317 "Timed out waiting for a Poolable to become " 318 + "available. Blocked for " + ( now - blockStart ) 319 + "ms. Thread: " + Thread.currentThread().getName() ); 320 } 321 throw new Exception 322 ( "Could not create enough Components to service your " 323 + "request (Timed out)." ); 324 } 325 } while( m_readySize == 0 ); 326 } 327 else 328 { 329 do 333 { 334 try 335 { 336 m_semaphore.wait(); 337 } 338 catch( InterruptedException e ) 339 { 340 } 341 342 if( m_disposed ) 344 { 345 throw new IllegalStateException ( "Already Disposed" ); 346 } 347 } while( m_readySize == 0 ); 348 } 349 350 poolable = (Poolable)m_ready.removeLast(); 352 m_readySize--; 353 354 if( getLogger().isDebugEnabled() ) 355 { 356 long now = System.currentTimeMillis(); 357 getLogger().debug( "Blocked for " + ( now - blockStart ) + "ms " 358 + "waiting for a Poolable to become available. " 359 + "Thread: " + Thread.currentThread().getName() ); 360 } 361 } 362 else 363 { 364 throw new Exception 366 ( "Could not create enough Components to service your request." ); 367 } 368 } 369 else 370 { 371 poolable = newPoolable(); 374 m_size++; 375 376 if( getLogger().isDebugEnabled() ) 377 { 378 getLogger().debug( "Created a new " + poolable.getClass().getName() 379 + " from the object factory." ); 380 } 381 } 382 } 383 384 readySize = getReadySizeSync(); 385 } 386 387 if( getLogger().isDebugEnabled() ) 388 { 389 getLogger().debug( "Got a " + poolable.getClass().getName() + " from the pool." ); 390 } 391 392 m_getsInstrument.increment(); 394 m_readySizeInstrument.setValue( readySize ); 395 396 return poolable; 397 } 398 399 404 public void put( Poolable poolable ) 405 { 406 if( poolable instanceof Recyclable ) 408 { 409 ( (Recyclable)poolable ).recycle(); 410 } 411 412 int readySize; 413 synchronized( m_semaphore ) 414 { 415 if( m_size <= m_max ) 416 { 417 if( m_disposed ) 418 { 419 if( getLogger().isDebugEnabled() ) 421 { 422 getLogger().debug( "Put called for a " + poolable.getClass().getName() 423 + " after the pool was disposed." ); 424 } 425 426 permanentlyRemovePoolable( poolable ); 427 } 428 else 429 { 430 if( getLogger().isDebugEnabled() ) 432 { 433 getLogger().debug( "Put a " + poolable.getClass().getName() 434 + " back into the pool." ); 435 } 436 437 m_ready.addLast( poolable ); 438 m_readySize++; 439 440 if( m_blocking ) 442 { 443 m_semaphore.notify(); 444 } 445 } 446 } 447 else 448 { 449 if( getLogger().isDebugEnabled() ) 451 { 452 getLogger().debug( "No room to put a " + poolable.getClass().getName() 453 + " back into the pool, so remove it." ); 454 } 455 456 permanentlyRemovePoolable( poolable ); 457 } 458 459 readySize = getReadySizeSync(); 460 } 461 462 m_putsInstrument.increment(); 464 m_readySizeInstrument.setValue( readySize ); 465 } 466 467 470 476 public void dispose() 477 { 478 m_disposed = true; 479 480 int size; 482 int readySize; 483 synchronized( m_semaphore ) 484 { 485 for( Iterator iter = m_ready.iterator(); iter.hasNext(); ) 487 { 488 Poolable poolable = (Poolable)iter.next(); 489 iter.remove(); 490 m_readySize--; 491 permanentlyRemovePoolable( poolable ); 492 } 493 494 if( m_oldReady != null ) 496 { 497 for( Iterator iter = m_oldReady.iterator(); iter.hasNext(); ) 498 { 499 Poolable poolable = (Poolable)iter.next(); 500 iter.remove(); 501 m_oldReadySize--; 502 permanentlyRemovePoolable( poolable ); 503 } 504 } 505 506 if( m_blocking ) 508 { 509 m_semaphore.notifyAll(); 510 } 511 512 if( ( m_size > 0 ) && getLogger().isDebugEnabled() ) 513 { 514 getLogger().debug( "There were " + m_size 515 + " outstanding objects when the pool was disposed." ); 516 } 517 518 size = getSize(); 519 readySize = getReadySizeSync(); 520 } 521 522 m_sizeInstrument.setValue( size ); 524 m_readySizeInstrument.setValue( readySize ); 525 } 526 527 530 543 public void setInstrumentableName( String name ) 544 { 545 m_instrumentableName = name; 546 } 547 548 553 public String getInstrumentableName() 554 { 555 return m_instrumentableName; 556 } 557 558 569 public Instrument[] getInstruments() 570 { 571 return new Instrument[] 572 { 573 m_sizeInstrument, 574 m_readySizeInstrument, 575 m_getsInstrument, 576 m_putsInstrument, 577 m_blocksInstrument, 578 m_createsInstrument, 579 m_decommissionsInstrument 580 }; 581 } 582 583 592 public Instrumentable[] getChildInstrumentables() 593 { 594 return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY; 595 } 596 597 600 606 protected void permanentlyRemovePoolable( Poolable poolable ) 607 { 608 m_size--; 609 removePoolable( poolable ); 610 } 611 612 617 public int getSize() 618 { 619 return m_size; 620 } 621 622 628 private int getReadySizeSync() 629 { 630 return m_readySize + m_oldReadySize; 631 } 632 633 638 public int getReadySize() 639 { 640 synchronized( m_semaphore ) 641 { 642 return getReadySizeSync(); 643 } 644 } 645 646 655 protected Poolable newPoolable() throws Exception 656 { 657 Object obj = m_factory.newInstance(); 658 659 m_createsInstrument.increment(); 661 m_sizeInstrument.setValue( getSize() + 1 ); 663 664 return (Poolable)obj; 665 } 666 667 676 protected void removePoolable( Poolable poolable ) 677 { 678 try 679 { 680 m_factory.decommission( poolable ); 681 682 m_decommissionsInstrument.increment(); 684 m_sizeInstrument.setValue( getSize() ); 685 } 686 catch( Exception e ) 687 { 688 if( getLogger().isDebugEnabled() ) 689 { 690 getLogger().debug( "Error decommissioning object", e ); 691 } 692 } 693 } 694 695 723 public int trim() 724 { 725 if( m_oldReady != null ) 726 { 727 synchronized( m_semaphore ) 728 { 729 return trimInner(); 730 } 731 } 732 else 733 { 734 throw new IllegalStateException ( "This pool is not configured to do trimming." ); 735 } 736 } 737 738 743 private int trimInner() 744 { 745 int trimCount = 0; 746 747 if( m_oldReadySize > 0 ) 749 { 750 if( getLogger().isDebugEnabled() ) 751 { 752 getLogger().debug( "Trimming " + m_oldReadySize + " idle objects from pool." ); 753 } 754 755 trimCount = m_oldReadySize; 756 757 for( Iterator iter = m_oldReady.iterator(); iter.hasNext(); ) 758 { 759 Poolable poolable = (Poolable)iter.next(); 760 iter.remove(); 761 m_oldReadySize--; 762 permanentlyRemovePoolable( poolable ); 763 } 764 } 765 766 if( getLogger().isDebugEnabled() ) 768 { 769 getLogger().debug( "Marking " + m_readySize + " objects as old in pool." ); 770 } 771 LinkedList tempList = m_oldReady; 772 m_oldReady = m_ready; 773 m_oldReadySize = m_readySize; 774 m_ready = tempList; 775 m_readySize = 0; 776 777 m_lastTrim = System.currentTimeMillis(); 778 779 return trimCount; 780 } 781 } 782 | Popular Tags |