1 50 package org.apache.avalon.excalibur.pool; 51 52 import java.util.Iterator ; 53 import java.util.LinkedList ; 54 55 import org.apache.avalon.framework.activity.Disposable; 56 import org.apache.avalon.framework.logger.AbstractLogEnabled; 57 import org.apache.avalon.framework.logger.LogEnabled; 58 import org.apache.avalon.framework.thread.ThreadSafe; 59 import org.apache.excalibur.instrument.CounterInstrument; 60 import org.apache.excalibur.instrument.Instrument; 61 import org.apache.excalibur.instrument.Instrumentable; 62 import org.apache.excalibur.instrument.ValueInstrument; 63 64 79 public class ResourceLimitingPool 80 extends AbstractLogEnabled 81 implements Pool, LogEnabled, Disposable, ThreadSafe, Instrumentable 82 { 83 public static final String DEFAULT_INSTRUMENTABLE_NAME = "pool"; 84 public static final String INSTRUMENT_SIZE_NAME = "size"; 85 public static final String INSTRUMENT_READY_SIZE_NAME = "ready-size"; 86 public static final String INSTRUMENT_GETS_NAME = "gets"; 87 public static final String INSTRUMENT_PUTS_NAME = "puts"; 88 public static final String INSTRUMENT_BLOCKS_NAME = "blocks"; 89 public static final String INSTRUMENT_CREATES_NAME = "creates"; 90 public static final String INSTRUMENT_DECOMMISSIONS_NAME = "decommissions"; 91 92 95 98 protected final Object m_semaphore = new Object (); 99 100 103 106 private boolean m_disposed = false; 107 108 111 private final ObjectFactory m_factory; 112 113 116 private final int m_max; 117 118 121 private final boolean m_maxStrict; 122 123 127 private final boolean m_blocking; 128 129 133 private final long m_blockTimeout; 134 135 138 private final long m_trimInterval; 139 140 143 private long m_lastTrim; 144 145 148 private LinkedList m_ready; 149 150 153 private int m_readySize; 154 155 158 private LinkedList m_oldReady; 159 160 163 private int m_oldReadySize; 164 165 168 private int m_size; 169 170 171 private String m_instrumentableName = DEFAULT_INSTRUMENTABLE_NAME; 172 173 174 private ValueInstrument m_sizeInstrument; 175 176 177 private ValueInstrument m_readySizeInstrument; 178 179 180 private CounterInstrument m_getsInstrument; 181 182 183 private CounterInstrument m_putsInstrument; 184 185 186 private CounterInstrument m_blocksInstrument; 187 188 189 private CounterInstrument m_createsInstrument; 190 191 192 private CounterInstrument m_decommissionsInstrument; 193 194 197 213 public ResourceLimitingPool( final ObjectFactory factory, 214 int max, 215 boolean maxStrict, 216 boolean blocking, 217 long blockTimeout, 218 long trimInterval ) 219 { 220 m_factory = factory; 221 m_max = ( max <= 0 ? Integer.MAX_VALUE : max ); 222 m_maxStrict = maxStrict; 223 m_blocking = blocking; 224 m_blockTimeout = blockTimeout; 225 m_trimInterval = trimInterval; 226 227 m_ready = new LinkedList (); 229 if( m_trimInterval > 0 ) 230 { 231 m_oldReady = new LinkedList (); 232 } 233 234 m_sizeInstrument = new ValueInstrument( INSTRUMENT_SIZE_NAME ); 236 m_readySizeInstrument = new ValueInstrument( INSTRUMENT_READY_SIZE_NAME ); 237 m_getsInstrument = new CounterInstrument( INSTRUMENT_GETS_NAME ); 238 m_putsInstrument = new CounterInstrument( INSTRUMENT_PUTS_NAME ); 239 m_blocksInstrument = new CounterInstrument( INSTRUMENT_BLOCKS_NAME ); 240 m_createsInstrument = new CounterInstrument( INSTRUMENT_CREATES_NAME ); 241 m_decommissionsInstrument = new CounterInstrument( INSTRUMENT_DECOMMISSIONS_NAME ); 242 } 243 244 247 257 public Poolable get() throws Exception 258 { 259 if( m_disposed ) throw new IllegalStateException ( "Already Disposed" ); 260 261 Poolable poolable; 262 synchronized( m_semaphore ) 263 { 264 if( ( m_oldReady != null ) && 266 ( System.currentTimeMillis() - m_lastTrim >= m_trimInterval ) ) 267 { 268 trimInner(); 269 } 270 271 if( m_readySize > 0 ) 273 { 274 poolable = (Poolable)m_ready.removeLast(); 276 m_readySize--; 277 } 278 else if( m_oldReadySize > 0 ) 279 { 280 poolable = (Poolable)m_oldReady.removeLast(); 282 m_oldReadySize--; 283 } 284 else 285 { 286 if( ( m_size >= m_max ) && m_maxStrict ) 288 { 289 292 if( m_blocking ) 294 { 295 long blockStart = System.currentTimeMillis(); 296 297 if( getLogger().isDebugEnabled() ) 298 { 299 getLogger().debug( "Blocking until a Poolable is available. " 300 + "Thread: " + Thread.currentThread().getName() ); 301 } 302 303 m_blocksInstrument.increment(); 305 306 if( m_blockTimeout > 0 ) 307 { 308 long blockWait = m_blockTimeout; 313 do 314 { 315 if( blockWait > 0 ) 316 { 317 try 318 { 319 m_semaphore.wait( blockWait ); 320 } 321 catch( InterruptedException e ) 322 { 323 } 324 325 if( m_disposed ) 327 { 328 throw new IllegalStateException ( "Already Disposed" ); 329 } 330 331 if( m_readySize == 0 ) 332 { 333 long now = System.currentTimeMillis(); 335 blockWait = m_blockTimeout - ( now - blockStart ); 336 } 337 } 338 else 339 { 340 long now = System.currentTimeMillis(); 342 343 if( getLogger().isDebugEnabled() ) 344 { 345 getLogger().debug( 346 "Timed out waiting for a Poolable to become " 347 + "available. Blocked for " + ( now - blockStart ) 348 + "ms. Thread: " + Thread.currentThread().getName() ); 349 } 350 throw new Exception 351 ( "Could not create enough Components to service your " 352 + "request (Timed out)." ); 353 } 354 } while( m_readySize == 0 ); 355 } 356 else 357 { 358 do 362 { 363 try 364 { 365 m_semaphore.wait(); 366 } 367 catch( InterruptedException e ) 368 { 369 } 370 371 if( m_disposed ) 373 { 374 throw new IllegalStateException ( "Already Disposed" ); 375 } 376 } while( m_readySize == 0 ); 377 } 378 379 poolable = (Poolable)m_ready.removeLast(); 381 m_readySize--; 382 383 if( getLogger().isDebugEnabled() ) 384 { 385 long now = System.currentTimeMillis(); 386 getLogger().debug( "Blocked for " + ( now - blockStart ) + "ms " 387 + "waiting for a Poolable to become available. " 388 + "Thread: " + Thread.currentThread().getName() ); 389 } 390 } 391 else 392 { 393 throw new Exception 395 ( "Could not create enough Components to service your request." ); 396 } 397 } 398 else 399 { 400 m_size++; 402 poolable = newPoolable(); 403 404 if( getLogger().isDebugEnabled() ) 405 { 406 getLogger().debug( "Created a new " + poolable.getClass().getName() 407 + " from the object factory." ); 408 } 409 } 410 } 411 } 412 413 if( getLogger().isDebugEnabled() ) 414 { 415 getLogger().debug( "Got a " + poolable.getClass().getName() + " from the pool." ); 416 } 417 418 m_getsInstrument.increment(); 420 if( m_readySizeInstrument.isActive() ) 421 { 422 m_readySizeInstrument.setValue( getReadySize() ); 423 } 424 425 return poolable; 426 } 427 428 433 public void put( Poolable poolable ) 434 { 435 if( poolable instanceof Recyclable ) 437 { 438 ( (Recyclable)poolable ).recycle(); 439 } 440 441 synchronized( m_semaphore ) 442 { 443 if( m_size <= m_max ) 444 { 445 if( m_disposed ) 446 { 447 if( getLogger().isDebugEnabled() ) 449 { 450 getLogger().debug( "Put called for a " + poolable.getClass().getName() 451 + " after the pool was disposed." ); 452 } 453 454 permanentlyRemovePoolable( poolable ); 455 } 456 else 457 { 458 if( getLogger().isDebugEnabled() ) 460 { 461 getLogger().debug( "Put a " + poolable.getClass().getName() 462 + " back into the pool." ); 463 } 464 465 m_ready.addLast( poolable ); 466 m_readySize++; 467 468 if( m_blocking ) 470 { 471 m_semaphore.notify(); 472 } 473 } 474 } 475 else 476 { 477 if( getLogger().isDebugEnabled() ) 479 { 480 getLogger().debug( "No room to put a " + poolable.getClass().getName() 481 + " back into the pool, so remove it." ); 482 } 483 484 permanentlyRemovePoolable( poolable ); 485 } 486 } 487 488 m_putsInstrument.increment(); 490 if( m_readySizeInstrument.isActive() ) 491 { 492 m_readySizeInstrument.setValue( getReadySize() ); 493 } 494 } 495 496 499 505 public void dispose() 506 { 507 m_disposed = true; 508 509 synchronized( m_semaphore ) 511 { 512 for( Iterator iter = m_ready.iterator(); iter.hasNext(); ) 514 { 515 Poolable poolable = (Poolable)iter.next(); 516 iter.remove(); 517 m_readySize--; 518 permanentlyRemovePoolable( poolable ); 519 } 520 521 if( m_oldReady != null ) 523 { 524 for( Iterator iter = m_oldReady.iterator(); iter.hasNext(); ) 525 { 526 Poolable poolable = (Poolable)iter.next(); 527 iter.remove(); 528 m_oldReadySize--; 529 permanentlyRemovePoolable( poolable ); 530 } 531 } 532 533 if( m_blocking ) 535 { 536 m_semaphore.notifyAll(); 537 } 538 539 if( ( m_size > 0 ) && getLogger().isDebugEnabled() ) 540 { 541 getLogger().debug( "There were " + m_size 542 + " outstanding objects when the pool was disposed." ); 543 } 544 545 if( m_sizeInstrument.isActive() ) 547 { 548 m_sizeInstrument.setValue( getSize() ); 549 } 550 if( m_readySizeInstrument.isActive() ) 551 { 552 m_readySizeInstrument.setValue( getReadySize() ); 553 } 554 } 555 } 556 557 560 573 public void setInstrumentableName( String name ) 574 { 575 m_instrumentableName = name; 576 } 577 578 583 public String getInstrumentableName() 584 { 585 return m_instrumentableName; 586 } 587 588 599 public Instrument[] getInstruments() 600 { 601 return new Instrument[] 602 { 603 m_sizeInstrument, 604 m_readySizeInstrument, 605 m_getsInstrument, 606 m_putsInstrument, 607 m_blocksInstrument, 608 m_createsInstrument, 609 m_decommissionsInstrument 610 }; 611 } 612 613 622 public Instrumentable[] getChildInstrumentables() 623 { 624 return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY; 625 } 626 627 630 636 protected void permanentlyRemovePoolable( Poolable poolable ) 637 { 638 m_size--; 639 removePoolable( poolable ); 640 } 641 642 645 public int getSize() 646 { 647 return m_size; 648 } 649 650 653 public int getReadySize() 654 { 655 synchronized( m_semaphore ) 656 { 657 return m_readySize + m_oldReadySize; 658 } 659 } 660 661 670 protected Poolable newPoolable() throws Exception 671 { 672 Object obj = m_factory.newInstance(); 673 674 m_createsInstrument.increment(); 676 if( m_sizeInstrument.isActive() ) 677 { 678 m_sizeInstrument.setValue( getSize() ); 679 } 680 681 return (Poolable)obj; 682 } 683 684 693 protected void removePoolable( Poolable poolable ) 694 { 695 try 696 { 697 m_factory.decommission( poolable ); 698 699 m_decommissionsInstrument.increment(); 701 if( m_sizeInstrument.isActive() ) 702 { 703 m_sizeInstrument.setValue( getSize() ); 704 } 705 } 706 catch( Exception e ) 707 { 708 if( getLogger().isDebugEnabled() ) 709 { 710 getLogger().debug( "Error decommissioning object", e ); 711 } 712 } 713 } 714 715 743 public int trim() 744 { 745 if( m_oldReady != null ) 746 { 747 synchronized( m_semaphore ) 748 { 749 return trimInner(); 750 } 751 } 752 else 753 { 754 throw new IllegalStateException ( "This pool is not configured to do trimming." ); 755 } 756 } 757 758 763 private int trimInner() 764 { 765 int trimCount = 0; 766 767 if( m_oldReadySize > 0 ) 769 { 770 if( getLogger().isDebugEnabled() ) 771 { 772 getLogger().debug( "Trimming " + m_oldReadySize + " idle objects from pool." ); 773 } 774 775 trimCount = m_oldReadySize; 776 777 for( Iterator iter = m_oldReady.iterator(); iter.hasNext(); ) 778 { 779 Poolable poolable = (Poolable)iter.next(); 780 iter.remove(); 781 m_oldReadySize--; 782 permanentlyRemovePoolable( poolable ); 783 } 784 } 785 786 if( getLogger().isDebugEnabled() ) 788 { 789 getLogger().debug( "Marking " + m_readySize + " objects as old in pool." ); 790 } 791 LinkedList tempList = m_oldReady; 792 m_oldReady = m_ready; 793 m_oldReadySize = m_readySize; 794 m_ready = tempList; 795 m_readySize = 0; 796 797 m_lastTrim = System.currentTimeMillis(); 798 799 return trimCount; 800 } 801 } 802 | Popular Tags |