1 5 package snaq.util; 6 7 import java.io.*; 8 import java.text.*; 9 import java.util.*; 10 11 42 public abstract class ObjectPool extends LogUtil 43 { 44 private static final int ACCESS_FIFO = 1; 46 private static final int ACCESS_LIFO = 2; 47 private static final int ACCESS_RANDOM = 3; 48 private int accessMethod = ACCESS_LIFO; 49 private String name; 51 private List free, used; 52 private int poolSize, maxSize; 53 private long expiryTime; 54 private long requests, hits; 55 private boolean released = false; 56 private boolean asyncDestroy = false; 57 private DateFormat dateFormat; 58 private Cleaner cleaner; 59 private InitThread initer; 60 private static int cleanerCount = 0; 61 private List listeners = new ArrayList(); 62 63 64 71 protected ObjectPool(String name, int poolSize, int maxSize, long expiryTime) 72 { 73 Class type = getPoolClass(); 74 if (!List.class.isAssignableFrom(type)) 75 throw new RuntimeException ("Invalid pool class type specified: " + type.getName() + " (must implement java.util.List)"); 76 try 77 { 78 free = (List)type.newInstance(); 79 used = (List)type.newInstance(); 80 } 81 catch (Exception e) 82 { 83 throw new RuntimeException ("Unable to instantiate pool class type: " + type.getName()); 84 } 85 this.name = name; 86 this.setParameters(poolSize, maxSize, expiryTime); 87 } 88 89 96 protected ObjectPool(String name, int poolSize, int maxSize, int expiryTime) 97 { 98 this(name, poolSize, maxSize, (long)expiryTime); 99 } 100 101 102 106 public final void init(int num) 107 { 108 if (num == 0) 109 return; 110 if (num > 0 && num <= getMaxSize()) 111 { 112 if (initer != null) 113 { 114 initer.halt(); 115 try { initer.join(); } catch (InterruptedException ie) {} 116 } 117 initer = new InitThread(num); 118 initer.start(); 119 } 120 else 121 throw new IllegalArgumentException ("Invalid number of items specified for initialization"); 122 } 123 124 133 protected final synchronized Reusable checkOut() throws Exception 134 { 135 if (released) 136 throw new RuntimeException ("Pool no longer valid for use"); 137 int oldTotalConns = used.size() + free.size(); 138 139 TimeWrapper tw = null; 140 Reusable o = null; 141 if (free.size() > 0) 142 { 143 switch(accessMethod) 145 { 146 case ACCESS_FIFO: 147 tw = (TimeWrapper)free.remove(0); 148 break; 149 case ACCESS_RANDOM: 150 tw = (TimeWrapper)free.remove((int)(free.size() * Math.random())); 151 break; 152 case ACCESS_LIFO: 153 default: 154 tw = (TimeWrapper)free.remove(free.size() - 1); 155 } 156 o = (Reusable)tw.getObject(); 157 boolean valid = isValid(o); 158 while (!valid && free.size() > 0) 159 { 160 destroyObject(o); 161 log("Removed invalid item from pool"); 162 tw = (TimeWrapper)free.remove(0); 163 o = (Reusable)tw.getObject(); 164 valid = isValid(o); 165 } 166 if (free.size() == 0 && !valid) 167 o = null; 168 } 169 boolean hit = (o != null); 170 171 if (o == null) 173 { 174 if (maxSize > 0 && used.size() == maxSize) 175 fireMaxSizeLimitErrorEvent(); 176 else if (used.size() < maxSize || maxSize == 0) 177 { 178 o = create(); 179 if (!isValid(o)) 180 throw new RuntimeException ("Unable to create a valid connection"); 181 } 182 } 183 184 if (o != null) 186 { 187 used.add(o); 188 requests++; 189 if (hit) 190 hits++; 191 firePoolCheckOutEvent(); 192 int totalConns = used.size() + free.size(); 195 if (totalConns == poolSize && totalConns > oldTotalConns) 196 fireMaxPoolLimitReachedEvent(); 197 else if (totalConns == poolSize + 1 && totalConns > oldTotalConns) 198 fireMaxPoolLimitExceededEvent(); 199 if (totalConns == maxSize && totalConns > oldTotalConns) 200 fireMaxSizeLimitReachedEvent(); 201 } 202 if (debug) 203 { 204 String ratio = used.size() + "/" + (used.size() + free.size()); 205 String hitRate = " (HitRate=" + getHitRate() + "%)"; 206 log("Checkout - " + ratio + hitRate + (o == null ? " - null returned" : "")); 207 } 208 return o; 209 } 210 211 212 225 protected final synchronized Reusable checkOut(long timeout) throws Exception 226 { 227 long time = System.currentTimeMillis(); 228 Reusable o = null; 229 o = checkOut(); 230 while (o == null && (System.currentTimeMillis() - time < timeout)) 231 { 232 try 233 { 234 if (debug) 235 log("No pooled items spare...waiting for up to " + timeout + "ms"); 236 wait(timeout); 237 o = checkOut(); 238 } 239 catch (InterruptedException e) { log(e, "Connection checkout interrupted"); } 240 } 241 return o; 242 } 243 244 245 250 protected final void checkIn(Reusable o) 251 { 252 if (o == null) 253 { 254 log("Attempt to return null item"); 255 return; 256 } 257 258 synchronized(this) 259 { 260 firePoolCheckInEvent(); 261 262 if (!used.remove(o)) 264 { 265 log("Attempt to return item not belonging to pool"); 266 throw new RuntimeException ("Attempt to return item not belonging to pool " + name); 267 } 268 269 boolean kill = maxSize > 0 && (free.size() + used.size() >= poolSize); 271 kill = kill || (maxSize == 0 && free.size() >= poolSize); 272 if (kill) 273 { 274 destroyObject(o); 275 if (debug) 276 log("Checkin* - " + used.size() + "/" + (used.size()+free.size())); 277 } 278 else 279 { 280 try 281 { 282 o.recycle(); 284 free.add(new TimeWrapper(null, o, expiryTime)); 286 if (debug) 287 log("Checkin - " + used.size() + "/" + (used.size()+free.size())); 288 notifyAll(); 289 } 290 catch (Exception e) 291 { 292 destroyObject(o); 294 log(e, "Unable to recycle item - destroyed"); 295 } 296 } 297 } 298 } 299 300 301 306 public final void release() { release(false); } 307 308 312 public final synchronized void releaseAsync() { releaseAsync(false); } 313 314 319 public final void releaseForcibly() { release(true); } 320 321 325 private final void release(boolean forced) 326 { 327 if (released) 329 return; 330 released = true; 331 if (cleaner != null) 333 { 334 cleaner.halt(); 335 try { cleaner.join(); } 336 catch (InterruptedException ie) { log(ie, "Interrupted during halting of old cleaner thread"); } 337 cleaner = null; 338 } 339 340 synchronized(this) 341 { 342 int rel = 0, failed = 0; 343 TimeWrapper tw = null; 344 Reusable o = null; 345 if (forced) 347 { 348 for (Iterator iter = used.iterator(); iter.hasNext();) 349 { 350 o = (Reusable)iter.next(); 351 try 352 { 353 destroy(o); 354 rel++; 355 } 356 catch (Exception ex) 357 { 358 failed++; 359 log(ex, "Unable to release item in pool"); 360 } 361 } 362 used.clear(); 363 } 364 else 365 { 366 if (debug && used.size() > 0) 367 log("Waiting for used items to be checked-in..."); 368 while (used.size() > 0) 369 { 370 try { wait(); } 371 catch (InterruptedException e) {} 372 } 373 } 374 375 for (Iterator iter = free.iterator(); iter.hasNext();) 377 { 378 tw = (TimeWrapper)iter.next(); 379 o = (Reusable)tw.getObject(); 380 try 381 { 382 destroy(o); 383 rel++; 384 } 385 catch (Exception ex) 386 { 387 failed++; 388 log(ex, "Unable to release item in pool"); 389 } 390 } 391 free.clear(); 392 393 if (debug) 395 { 396 String s = "Released " + rel + (rel > 1 ? " items" : " item"); 397 if (failed > 0) 398 s += " (failed to release " + failed + (failed > 1 ? " items)" : " item)"); 399 log(s); 400 } 401 firePoolReleasedEvent(); 402 listeners.clear(); 403 super.close(); 404 } 405 } 406 407 411 private final void releaseAsync(final boolean forced) 412 { 413 Thread t = new Thread (new Runnable () 414 { 415 public void run() { release(forced); } 416 }); 417 t.start(); 418 } 419 420 421 427 protected abstract Reusable create() throws Exception ; 428 429 435 protected abstract boolean isValid(final Reusable o); 436 437 442 protected abstract void destroy(final Reusable o); 443 444 447 private final void destroyObject(final Reusable o) 448 { 449 if (o == null) 450 return; 451 if (asyncDestroy) 452 { 453 Thread t = new Thread (new Runnable () 454 { 455 public void run() { destroy(o); } 456 }); 457 t.start(); 458 } 459 else 460 destroy(o); 461 } 462 463 471 public final void setAsyncDestroy(boolean b) { asyncDestroy = b; } 472 473 477 public final boolean isAsyncDestroy() { return asyncDestroy; } 478 479 482 public void log(String logEntry) 483 { 484 log(name + ": ", logEntry); 485 } 486 487 490 public void log(Throwable e, String logEntry) 491 { 492 log(e, name + ": ", logEntry); 493 } 494 495 498 public final String getName() { return this.name; } 499 500 503 public final int getPoolSize() { return poolSize; } 504 505 508 public final int getMaxSize() { return maxSize; } 509 510 513 public final long getExpiryTime() { return expiryTime; } 514 515 523 public final void setParameters(int poolSize, int maxSize, long expiryTime) 524 { 525 synchronized(this) 526 { 527 if (cleaner != null) 528 cleaner.halt(); 529 530 this.poolSize = Math.max(poolSize, 0); 531 this.maxSize = Math.max(maxSize, 0); 532 this.expiryTime = Math.max(expiryTime, 0); 533 if (this.maxSize > 0 && this.maxSize < this.poolSize) 534 this.maxSize = this.poolSize; 535 resetHitCounter(); 536 537 TimeWrapper tw = null; 539 for (Iterator iter = free.iterator(); iter.hasNext();) 540 { 541 tw = (TimeWrapper)iter.next(); 542 tw.setLiveTime(expiryTime); 543 } 544 if (this.expiryTime > 0) 546 { 547 long iVal = Math.min(5000, this.expiryTime / 5); 548 (cleaner = new Cleaner(this, iVal)).start(); 549 } 550 } 551 if (debug) 552 { 553 String info = "pool=" + this.poolSize + ",max=" + this.maxSize + ",expiry="; 554 info += this.expiryTime == 0 ? "none" : this.expiryTime + "ms"; 555 log("Parameters changed (" + info + ")"); 556 } 557 fireParametersChangedEvent(); 558 } 559 560 563 public final synchronized int getSize() { return free.size() + used.size(); } 564 565 568 public final synchronized int getCheckedOut() { return used.size(); } 569 570 573 public final synchronized int getFreeCount() { return free.size(); } 574 575 581 public final float getHitRate() { return (requests == 0) ? 0 : (((float)hits / requests) * 100f); } 582 583 586 protected final void resetHitCounter() { requests = hits = 0; } 587 588 591 protected final void setAccessFIFO() { accessMethod = ACCESS_FIFO; } 592 593 596 protected final void setAccessLIFO() { accessMethod = ACCESS_LIFO; } 597 598 601 protected final void setAccessRandom() { accessMethod = ACCESS_RANDOM; } 602 603 613 protected Class getPoolClass() { return ArrayList.class; } 614 615 619 public void finalize() 620 { 621 if (cleaner != null) 622 { 623 cleaner.halt(); 624 cleaner = null; 625 } 626 if (initer != null) 627 { 628 initer.halt(); 629 initer = null; 630 } 631 } 632 633 636 public final void flush() 637 { 638 int count = 0; 639 synchronized(this) 640 { 641 TimeWrapper tw = null; 642 for (Iterator iter = free.iterator(); iter.hasNext();) 643 { 644 tw = (TimeWrapper)iter.next(); 645 iter.remove(); 646 destroyObject((Reusable)tw.getObject()); 647 count++; 648 } 649 } 650 if (count > 0 && debug) 651 log("Flushed all spare items from pool"); 652 } 653 654 659 final synchronized boolean purge() 660 { 661 if (debug) 662 log("Checking for expired items"); 663 int count = 0; 664 TimeWrapper tw = null; 665 for (Iterator iter = free.iterator(); iter.hasNext();) 666 { 667 tw = (TimeWrapper)iter.next(); 668 if (tw.isExpired()) 669 { 670 iter.remove(); 671 destroyObject((Reusable)tw.getObject()); 672 count++; 673 } 674 } 675 return free.size() > 0 || count > 0; 676 } 677 678 682 685 public final void addObjectPoolListener(ObjectPoolListener x) 686 { 687 listeners.add(x); 688 } 689 690 693 public final void removeObjectPoolListener(ObjectPoolListener x) 694 { 695 listeners.remove(x); 696 } 697 698 private final void firePoolCheckOutEvent() 699 { 700 if (listeners.isEmpty()) 701 return; 702 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.CHECKOUT); 703 for (Iterator iter = listeners.iterator(); iter.hasNext();) 704 ((ObjectPoolListener)iter.next()).poolCheckOut(poolEvent); 705 } 706 707 private final void firePoolCheckInEvent() 708 { 709 if (listeners.isEmpty()) 710 return; 711 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.CHECKIN); 712 for (Iterator iter = listeners.iterator(); iter.hasNext();) 713 ((ObjectPoolListener)iter.next()).poolCheckIn(poolEvent); 714 } 715 716 private final void fireMaxPoolLimitReachedEvent() 717 { 718 if (listeners.isEmpty()) 719 return; 720 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_POOL_LIMIT_REACHED); 721 for (Iterator iter = listeners.iterator(); iter.hasNext();) 722 ((ObjectPoolListener)iter.next()).maxPoolLimitReached(poolEvent); 723 } 724 725 private final void fireMaxPoolLimitExceededEvent() 726 { 727 if (listeners.isEmpty()) 728 return; 729 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_POOL_LIMIT_EXCEEDED); 730 for (Iterator iter = listeners.iterator(); iter.hasNext();) 731 ((ObjectPoolListener)iter.next()).maxPoolLimitExceeded(poolEvent); 732 } 733 734 private final void fireMaxSizeLimitReachedEvent() 735 { 736 if (listeners.isEmpty()) 737 return; 738 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_SIZE_LIMIT_REACHED); 739 for (Iterator iter = listeners.iterator(); iter.hasNext();) 740 ((ObjectPoolListener)iter.next()).maxSizeLimitReached(poolEvent); 741 } 742 743 private final void fireMaxSizeLimitErrorEvent() 744 { 745 if (listeners.isEmpty()) 746 return; 747 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_SIZE_LIMIT_ERROR); 748 for (Iterator iter = listeners.iterator(); iter.hasNext();) 749 ((ObjectPoolListener)iter.next()).maxSizeLimitError(poolEvent); 750 } 751 752 private final void fireParametersChangedEvent() 753 { 754 if (listeners == null || listeners.isEmpty()) 755 return; 756 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.PARAMETERS_CHANGED); 757 for (Iterator iter = listeners.iterator(); iter.hasNext();) 758 ((ObjectPoolListener)iter.next()).poolParametersChanged(poolEvent); 759 } 760 761 private final void firePoolReleasedEvent() 762 { 763 if (listeners.isEmpty()) 764 return; 765 ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.POOL_RELEASED); 766 for (Iterator iter = listeners.iterator(); iter.hasNext();) 767 ((ObjectPoolListener)iter.next()).poolReleased(poolEvent); 768 } 769 770 771 776 final class Cleaner extends Thread 777 { 778 private ObjectPool pool; 779 private long interval; 780 private boolean stopped; 781 782 Cleaner(ObjectPool pool, long interval) 783 { 784 this.setName("CleanerThread_" + Integer.toString(cleanerCount++)); 785 this.pool = pool; 786 this.interval = interval; 787 } 788 789 public void start() 790 { 791 stopped = false; 792 super.start(); 793 } 794 795 798 public void halt() 799 { 800 if (!isHalted()) 801 { 802 stopped = true; 803 interrupt(); } 805 } 806 807 810 public boolean isHalted() { return stopped; } 811 812 815 public void run() 816 { 817 while (pool.cleaner == Thread.currentThread() && !stopped) 818 { 819 synchronized(pool) 820 { 821 if (!pool.purge()) 822 { 823 try { pool.wait(); } 824 catch (InterruptedException e) {} 825 } 826 } 827 if (!stopped) 828 { 829 try { sleep(interval); } 830 catch (InterruptedException e) {} 831 } 832 } 833 } 834 } 835 836 837 842 private class InitThread extends Thread 843 { 844 private int num; 845 private boolean stopped = false; 846 847 private InitThread(int num) 848 { 849 this.num = Math.min(poolSize, Math.max(num, 0)); 851 } 852 853 public void halt() { stopped = true; } 854 855 860 public void run() 861 { 862 if (num > 0 && num <= poolSize && getSize() < num) 863 { 864 int count = 0; 865 while (!stopped && getSize() < num && num <= poolSize) 866 { 867 try 868 { 869 Reusable o = create(); 870 if (o == null) 871 throw new RuntimeException ("Null item created"); 872 else 873 { 874 free.add(new TimeWrapper(null, o, expiryTime)); 875 count++; 876 if (debug) 877 log("Initialized new item in pool"); 878 } 879 } 880 catch (Exception ex) 881 { 882 log(ex, "Unable to initialize items in pool"); 883 stopped = true; 884 } 885 } 886 if (debug) 887 log("Initialized pool with " + count + " new item" + (count > 1 ? "s" : "")); 888 } 889 } 890 } 891 } | Popular Tags |