1 4 package org.ofbiz.minerva.pool; 5 6 7 import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore; 8 9 import java.util.ArrayList ; 10 import java.util.Collection ; 11 import java.util.Collections ; 12 import java.util.ConcurrentModificationException ; 13 import java.util.HashMap ; 14 import java.util.HashSet ; 15 import java.util.Iterator ; 16 import java.util.Map ; 17 import java.util.Set ; 18 19 import org.apache.log4j.Logger; 20 21 44 public class ObjectPool implements PoolEventListener { 45 46 private final static String INITIALIZED = "Pool already initialized!"; 47 private final static PoolGCThread collector = new PoolGCThread(); 48 49 static { 50 collector.start(); 51 } 52 53 private Logger log = Logger.getLogger(ObjectPool.class); 54 private PoolObjectFactory factory; 55 private String poolName; 56 57 private final Map objects = new HashMap (); 58 private final Set deadObjects = Collections.synchronizedSet(new HashSet ()); 59 private int minSize = 0; 60 private int maxSize = 0; 61 private boolean idleTimeout = false; 62 private boolean runGC = false; 63 private float maxIdleShrinkPercent = 1.0f; private long idleTimeoutMillis = 1800000l; private long gcMinIdleMillis = 1200000l; private long gcIntervalMillis = 120000l; private long lastGC = System.currentTimeMillis(); 68 private boolean blocking = true; 69 private int blockingTimeout = 10000; private boolean trackLastUsed = false; 71 private boolean invalidateOnError = false; 72 73 private FIFOSemaphore permits; 74 private boolean initialized = false; 75 76 83 public ObjectPool() { 84 } 85 86 96 public ObjectPool(PoolObjectFactory factory, String poolName) { 97 setObjectFactory(factory); 98 setName(poolName); 99 } 100 101 114 public ObjectPool(Class javaBeanClass, String poolName) { 115 setObjectFactory(javaBeanClass); 116 setName(poolName); 117 } 118 119 127 public void setObjectFactory(PoolObjectFactory factory) { 128 if (initialized) 129 throw new IllegalStateException (INITIALIZED); 130 this.factory = factory; 131 } 132 133 142 public void setObjectFactory(Class javaBeanClass) { 143 if (initialized) 144 throw new IllegalStateException (INITIALIZED); 145 factory = new BeanFactory(javaBeanClass); 146 } 147 148 156 public void setName(String name) { 157 if (name == null || name.length() == 0) 158 throw new IllegalArgumentException ("Cannot set pool name to null or empty!"); 159 if (poolName != null && !poolName.equals(name)) 160 throw new IllegalStateException ("Cannot change pool name once set!"); 161 poolName = name; 162 log = Logger.getLogger(ObjectPool.class.getName() + "." + name); 163 } 164 165 168 public String getName() { 169 return poolName; 170 } 171 172 180 public void setMinSize(int size) { 181 if (initialized) 182 throw new IllegalStateException (INITIALIZED); 183 minSize = size; 184 if (maxSize != 0 && minSize > maxSize) { 185 maxSize = minSize; 186 log.warn("pool max size set to " + maxSize + " to stay >= min size"); 187 } 188 } 189 190 194 public int getMinSize() { 195 return minSize; 196 } 197 198 212 public void setMaxSize(int size) { 213 if (initialized) 214 throw new IllegalStateException (INITIALIZED); 215 maxSize = size; 216 if (maxSize != 0 && minSize > maxSize) { 217 minSize = maxSize; 218 log.warn("pool min size set to " + minSize + " to stay <= max size"); 219 } 220 } 221 222 226 public int getMaxSize() { 227 return maxSize; 228 } 229 230 250 public void setIdleTimeoutEnabled(boolean enableTimeout) { 251 if (initialized) 252 throw new IllegalStateException (INITIALIZED); 253 idleTimeout = enableTimeout; 254 } 255 256 263 public boolean isIdleTimeoutEnabled() { 264 return idleTimeout; 265 } 266 267 285 public void setGCEnabled(boolean enabled) { 286 if (initialized) 287 throw new IllegalStateException (INITIALIZED); 288 runGC = enabled; 289 } 290 291 295 public boolean isGCEnabled() { 296 return runGC; 297 } 298 299 316 public void setMaxIdleTimeoutPercent(float percent) { 317 if (initialized) 318 throw new IllegalStateException (INITIALIZED); 319 if (percent < 0f || percent > 1f) 320 throw new IllegalArgumentException ("Percent must be between 0 and 1!"); 321 maxIdleShrinkPercent = percent; 322 } 323 324 328 public float getMaxIdleTimeoutPercent() { 329 return maxIdleShrinkPercent; 330 } 331 332 345 public void setIdleTimeout(long millis) { 346 if (initialized) 347 throw new IllegalStateException (INITIALIZED); 348 idleTimeoutMillis = millis; 349 350 if (log.isDebugEnabled()) 351 log.debug("setIdleTimeout(" + millis + ")"); 352 } 353 354 358 public long getIdleTimeout() { 359 return idleTimeoutMillis; 360 } 361 362 376 public void setGCMinIdleTime(long millis) { 377 if (initialized) 378 throw new IllegalStateException (INITIALIZED); 379 gcMinIdleMillis = millis; 380 381 if (log.isDebugEnabled()) 382 log.debug("setGCMinIdleTime(" + millis + ")"); 383 } 384 385 390 public long getGCMinIdleTime() { 391 return gcMinIdleMillis; 392 } 393 394 408 public void setGCInterval(long millis) { 409 if (initialized) 410 throw new IllegalStateException (INITIALIZED); 411 412 gcIntervalMillis = millis; 413 414 if (log.isDebugEnabled()) 415 log.debug("setGCInterval(" + gcIntervalMillis + ")"); 416 } 417 418 422 public long getGCInterval() { 423 return gcIntervalMillis; 424 } 425 426 437 public void setBlocking(boolean blocking) { 438 if (initialized) 439 throw new IllegalStateException (INITIALIZED); 440 this.blocking = blocking; 441 } 442 443 448 public boolean isBlocking() { 449 return blocking; 450 } 451 452 455 public void setBlockingTimeout(int blockingTimeout) { 456 this.blockingTimeout = blockingTimeout; 457 } 458 459 460 public int getBlockingTimeout() { 461 return this.blockingTimeout; 462 } 463 464 474 public void setTimestampUsed(boolean timestamp) { 475 if (initialized) 476 throw new IllegalStateException (INITIALIZED); 477 478 trackLastUsed = timestamp; 479 480 if (log.isDebugEnabled()) 481 log.debug("setTimestampUsed(" + timestamp + ")"); 482 } 483 484 487 public boolean isTimestampUsed() { 488 return trackLastUsed; 489 } 490 491 502 public void setInvalidateOnError(boolean invalidate) { 503 if (initialized) 504 throw new IllegalStateException (INITIALIZED); 505 invalidateOnError = invalidate; 506 } 507 508 511 public boolean isInvalidateOnError() { 512 return invalidateOnError; 513 } 514 515 523 public void initialize() { 524 if (factory == null || poolName == null) 525 throw new IllegalStateException ("Factory and Name must be set before pool initialization!"); 526 if (initialized) 527 throw new IllegalStateException ("Cannot initialize more than once!"); 528 initialized = true; 529 permits = new FIFOSemaphore(maxSize); 530 factory.poolStarted(this); 531 lastGC = System.currentTimeMillis(); 532 fillToMin(); 534 546 collector.addPool(this); 547 } 548 549 555 public void shutDown() { 556 collector.removePool(this); 557 factory.poolClosing(this); 558 559 synchronized (objects) { 561 for (Iterator it = objects.values().iterator(); it.hasNext();) { 562 ObjectRecord rec = (ObjectRecord) it.next(); 563 if (null != rec) { 564 if (rec.isInUse()) 565 factory.returnObject(rec.getClientObject()); 566 factory.deleteObject(rec.getObject()); 567 rec.close(); 568 } 569 } 570 objects.clear(); 571 deadObjects.clear(); 572 } 574 factory = null; 575 poolName = null; 576 initialized = false; 577 } 578 579 586 public Object getObject() { 587 return getObject(null); 588 } 589 590 598 public Object getObject(Object parameters) { 599 if (objects == null) 600 throw new IllegalStateException ("Tried to use pool before it was Initialized or after it was ShutDown!"); 601 602 Object result = factory.isUniqueRequest(); 603 if (result != null) return result; 607 try { 608 if (permits.attempt(blockingTimeout)) { 609 ObjectRecord rec = null; 610 synchronized (objects) { 611 for (Iterator it = objects.values().iterator(); it.hasNext();) { 612 rec = (ObjectRecord) it.next(); 613 if (null != rec && !rec.isInUse() && factory.checkValidObject(rec.getObject(), parameters)) { 614 log.info("Handing out from pool object: " + rec.getObject()); 615 try { 616 rec.setInUse(true); 617 } catch (ConcurrentModificationException e) { 618 log.info("Conflict trying to set rec. in use flag:" + rec.getObject()); 619 continue; } 622 break; 623 } 624 rec = null; } 626 } 628 if (rec == null) { 629 try { 630 rec = createNewObject(parameters); 631 } catch (Exception e) { 632 log.error("Exception in creating new object for pool", e); 633 permits.release(); 634 throw e; 635 } 636 } if (rec == null) { 638 permits.release(); 639 String message = "Pool is broken, did not find or create an object"; 640 log.error(message); 641 throw new RuntimeException (message); 642 } Object ob = rec.getObject(); 644 645 result = factory.prepareObject(ob); 646 if (result != ob) rec.setClientObject(result); 647 if (result instanceof PooledObject) 648 ((PooledObject) result).addPoolEventListener(this); 649 650 log.debug("Pool " + this + " gave out object: " + result); 651 return result; 652 } else { 654 throw new RuntimeException ("No ManagedConnections Available!"); 656 } } catch (RuntimeException e) { 659 throw e; 660 } catch (InterruptedException ie) { 662 log.info("Interrupted while requesting permit!", new Exception ("stacktrace")); 663 throw new RuntimeException ("Interrupted while requesting permit!"); 664 } catch (Exception e) { 666 log.info("problem getting connection from pool", e); 667 throw new RuntimeException ("problem getting connection from pool " + e.getMessage()); 668 } } 670 671 682 public void setLastUsed(Object object) { 683 if (!trackLastUsed) return; 684 Object ob = null; 685 try { 686 ob = factory.translateObject(object); 687 } catch (Exception e) { 688 throw new IllegalArgumentException ("Pool " + getName() + " does not recognize object for last used time: " + object); 689 } 690 ObjectRecord rec = ob == null ? null : (ObjectRecord) objects.get(ob); 691 if (rec == null) 692 throw new IllegalArgumentException ("Pool " + getName() + " does not recognize object for last used time: " + object); 693 if (rec.isInUse()) 694 rec.setLastUsed(); 695 else 696 throw new IllegalStateException ("Cannot set last updated time for an object that's not in use!"); 697 } 698 699 709 public void markObjectAsInvalid(Object object) { 710 if (deadObjects == null) 711 throw new IllegalStateException ("Tried to use pool before it was Initialized or after it was ShutDown!"); 712 deadObjects.add(object); 714 } 715 716 724 public void releaseObject(Object object) { 725 726 log.debug("Pool " + this + " object released: " + object); 727 728 Object pooled = null; 729 try { 730 factory.returnObject(object); pooled = factory.translateObject(object); 732 } catch (Exception e) { 733 return; } 735 if (pooled == null) return; 737 boolean removed = false; 738 synchronized (objects) { 739 ObjectRecord rec = (ObjectRecord) objects.get(pooled); 740 if (rec == null) throw new IllegalArgumentException ("Object " + object + " is not in pool " + poolName + "!"); 742 if (!rec.isInUse()) return; if (object instanceof PooledObject) 744 ((PooledObject) object).removePoolEventListener(this); 745 removed = deadObjects.remove(object); 746 rec.setInUse(false); 747 if (removed) { 748 log.debug("Object was dead: " + object); 749 objects.remove(pooled); 750 rec.close(); 751 } 753 } if (removed) { 755 try { 756 factory.deleteObject(pooled); 757 } catch (Exception e) { 758 log.error("Pool " + this + " factory (" + factory.getClass().getName() + " delete error: ", e); 759 } 760 fillToMin(); 761 765 } 766 767 if (removed) 768 log.debug("Pool " + this + " destroyed object " + object + "."); 769 else 770 log.debug("Pool " + this + " returned object " + object + " to the pool."); 771 772 permits.release(); 773 782 } 783 784 private int getUsedCount() { 785 int total = 0; 786 synchronized (objects) { 787 for (Iterator it = new HashSet (objects.values()).iterator(); it.hasNext();) { 788 ObjectRecord or = (ObjectRecord) it.next(); 789 if (or != null && or.isInUse()) 790 ++total; 791 } 792 } 793 return total; 794 } 795 796 799 public String toString() { 800 return poolName + " [" + getUsedCount() + "/" + (objects == null ? 0 : objects.size()) + "/" + (maxSize == 0 ? "Unlimited" : Integer.toString(maxSize)) + "]"; 801 } 802 803 804 806 809 public void objectClosed(PoolEvent evt) { 810 releaseObject(evt.getSource()); 811 } 812 813 817 public void objectError(PoolEvent evt) { 818 if (invalidateOnError || evt.isCatastrophic()) 819 markObjectAsInvalid(evt.getSource()); 820 } 821 822 826 public void objectUsed(PoolEvent evt) { 827 if (!trackLastUsed) return; 828 setLastUsed(evt.getSource()); 829 } 830 831 long getNextGCMillis(long now) { 832 long t = lastGC + gcIntervalMillis - now; 833 834 log.debug("getNextGCMillis(): returning " + t); 835 836 if (!runGC) 837 return Long.MAX_VALUE; 838 839 return t; 840 } 841 842 boolean isTimeToGC() { 844 long now; 845 now = System.currentTimeMillis(); 846 847 log.debug("isTimeToGC(): " + (now >= lastGC + Math.round((float) gcIntervalMillis * 0.9f))); 848 849 return now >= lastGC + Math.round((float) gcIntervalMillis * 0.9f); 850 851 } 852 853 void runGCandShrink() { 854 855 log.debug("runGCandShrink(): runGC = " + runGC + "; idleTimeout = " + idleTimeout); 856 857 if (runGC || idleTimeout) { 858 HashSet objsCopy; 859 synchronized (objects) { 860 objsCopy = new HashSet (objects.values()); 861 } 862 863 if (runGC) { Iterator it = objsCopy.iterator(); 865 while (it.hasNext()) { 866 ObjectRecord rec = (ObjectRecord) it.next(); 867 if (rec != null && rec.isInUse() && rec.getMillisSinceLastUse() >= gcMinIdleMillis) { 868 releaseObject(rec.getClientObject()); 869 } 870 } 871 } 872 if (idleTimeout) { HashSet eligible = new HashSet (); 875 Iterator it = objsCopy.iterator(); 876 while (it.hasNext()) { 877 ObjectRecord rec = (ObjectRecord) it.next(); 878 if (rec != null && !rec.isInUse() && rec.getMillisSinceLastUse() > idleTimeoutMillis) 879 eligible.add(rec); 880 } 881 int max = Math.round(eligible.size() * maxIdleShrinkPercent); 883 if (max == 0 && eligible.size() > 0) max = 1; 884 int count = 0; 885 it = eligible.iterator(); 887 while (it.hasNext()) { 888 try { 889 ObjectRecord rec = (ObjectRecord) it.next(); 891 if (rec != null) { 892 rec.setInUse(true); Object pooled = rec.getObject(); 894 synchronized (objects) { 895 objects.remove(pooled); 896 } 897 try { 899 factory.deleteObject(pooled); 900 } catch (Exception e) { 901 log.error("Pool " + this + " factory (" + factory.getClass().getName() + " delete error: ", e); 902 } 903 rec.close(); 904 ++count; 905 } 906 fillToMin(); 907 912 } catch (ConcurrentModificationException e) { 913 } 914 } 915 } 916 } 917 lastGC = System.currentTimeMillis(); 918 } 919 920 924 932 939 private ObjectRecord createNewObject(Object parameters) { 940 Object ob = null; 941 try { 942 ob = factory.createObject(parameters); 943 } catch (Exception ex) { 944 throw new RuntimeException ("Could not create connection"); 945 } 946 if (ob != null) { ObjectRecord rec = new ObjectRecord(ob); 948 synchronized (objects) { 949 objects.put(ob, rec); 950 } 951 return rec; 952 } else { 953 throw new RuntimeException ("could not create new object!"); 954 } 955 } 956 957 public void fillToMin() { 958 Collection newMCs = new ArrayList (); 959 try { 960 while (objects.size() < minSize) { 961 newMCs.add(getObject(null)); 962 } } catch (Exception re) { 964 } for (Iterator i = newMCs.iterator(); i.hasNext();) { 967 releaseObject(i.next()); 968 } 970 } 971 972 } 973 974 class BeanFactory extends PoolObjectFactory { 975 976 private Class beanClass; 977 978 private Logger log = Logger.getLogger(BeanFactory.class); 979 980 public BeanFactory(Class beanClass) { 981 try { 982 beanClass.getConstructor(new Class [0]); 983 } catch (NoSuchMethodException e) { 984 throw new IllegalArgumentException ("Bean class doesn't have no-arg constructor!"); 985 } 986 this.beanClass = beanClass; 987 } 988 989 public void poolStarted(ObjectPool pool) { 990 super.poolStarted(pool); 991 } 992 993 public Object createObject(Object parameters) { 994 try { 995 return beanClass.newInstance(); 996 } catch (Exception e) { 997 log.error("Unable to create instance of " + beanClass.getName(), e); 998 } 999 return null; 1000 } 1001} 1002 | Popular Tags |