0001: /*
0002: * Licensed under the X license (see http://www.x.org/terms.htm)
0003: */
0004: package org.ofbiz.minerva.pool;
0005:
0006: import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
0007:
0008: import java.util.ArrayList;
0009: import java.util.Collection;
0010: import java.util.Collections;
0011: import java.util.ConcurrentModificationException;
0012: import java.util.HashMap;
0013: import java.util.HashSet;
0014: import java.util.Iterator;
0015: import java.util.Map;
0016: import java.util.Set;
0017:
0018: import org.apache.log4j.Logger;
0019:
0020: /**
0021: * A generic object pool. You must provide a PoolObjectFactory (or the class
0022: * of a Java Bean) so the pool knows what kind of objects to create. It has
0023: * many configurable parameters, such as the minimum and maximum size of the
0024: * pool, whether to enable idle timeouts, etc. If the pooled objects
0025: * implement PooledObject, they will automatically be returned to the pool at
0026: * the appropriate times.
0027: * <P>In general, the appropriate way to use a pool is:</P>
0028: * <OL>
0029: * <LI>Create it</LI>
0030: * <LI>Configure it (set factory, name, parameters, etc.)</LI>
0031: * <LI>Initialize it (once done, further configuration is not allowed)</LI>
0032: * <LI>Use it</LI>
0033: * <LI>Shut it down</LI>
0034: * </OL>
0035: * @see org.ofbiz.minerva.pool.PooledObject
0036: *
0037: * @author Aaron Mulder (ammulder@alumni.princeton.edu)
0038: * @author <a href="mailto:danch@nvisia.com">danch (Dan Christopherson)</a>
0039: *
0040: * Revision:
0041: * 20010701 danch added code for timeout in blocking.
0042: */
0043: public class ObjectPool implements PoolEventListener {
0044:
0045: private final static String INITIALIZED = "Pool already initialized!";
0046: private final static PoolGCThread collector = new PoolGCThread();
0047:
0048: static {
0049: collector.start();
0050: }
0051:
0052: private Logger log = Logger.getLogger(ObjectPool.class);
0053: private PoolObjectFactory factory;
0054: private String poolName;
0055:
0056: private final Map objects = new HashMap();
0057: private final Set deadObjects = Collections
0058: .synchronizedSet(new HashSet());
0059: private int minSize = 0;
0060: private int maxSize = 0;
0061: private boolean idleTimeout = false;
0062: private boolean runGC = false;
0063: private float maxIdleShrinkPercent = 1.0f; // don't replace idle connections that timeout
0064: private long idleTimeoutMillis = 1800000l; // must be idle in pool for 30 minutes
0065: private long gcMinIdleMillis = 1200000l; // must be unused by client for 20 minutes
0066: private long gcIntervalMillis = 120000l; // shrink & gc every 2 minutes
0067: private long lastGC = System.currentTimeMillis();
0068: private boolean blocking = true;
0069: private int blockingTimeout = 10000;//Integer.MAX_VALUE;//this is silly
0070: private boolean trackLastUsed = false;
0071: private boolean invalidateOnError = false;
0072:
0073: private FIFOSemaphore permits;
0074: private boolean initialized = false;
0075:
0076: /**
0077: * Creates a new pool. It cannot be used until you specify a name and
0078: * object factory or bean class, and initialize it.
0079: * @see #setName
0080: * @see #setObjectFactory
0081: * @see #initialize
0082: */
0083: public ObjectPool() {
0084: }
0085:
0086: /**
0087: * Creates a new pool with the specified parameters. It cannot be used
0088: * until you initialize it.
0089: * @param factory The object factory that will create the objects to go in
0090: * the pool.
0091: * @param poolName The name of the pool. This does not have to be unique
0092: * across all pools, but it is strongly recommended (and it may be a
0093: * requirement for certain uses of the pool).
0094: * @see #initialize
0095: */
0096: public ObjectPool(PoolObjectFactory factory, String poolName) {
0097: setObjectFactory(factory);
0098: setName(poolName);
0099: }
0100:
0101: /**
0102: * Creates a new pool with the specified parameters. It cannot be used
0103: * until you initialize it.
0104: * @param javaBeanClass The Class of a Java Bean. New instances for the
0105: * pool will be created with the no-argument constructor, and no
0106: * particular initialization or cleanup will be performed on the
0107: * instances. Use a PoolObjectFactory if you want more control over
0108: * the instances.
0109: * @param poolName The name of the pool. This does not have to be unique
0110: * across all pools, but it is strongly recommended (and it may be a
0111: * requirement for certain uses of the pool).
0112: * @see #initialize
0113: */
0114: public ObjectPool(Class javaBeanClass, String poolName) {
0115: setObjectFactory(javaBeanClass);
0116: setName(poolName);
0117: }
0118:
0119: /**
0120: * Sets the object factory for the pool. The object factory controls the
0121: * instances created for the pool, and can initialize instances given out
0122: * by the pool and cleanup instances returned to the pool.
0123: * @throws java.lang.IllegalStateException
0124: * Occurs when you try to set the object factory after the pool has been
0125: * initialized.
0126: */
0127: public void setObjectFactory(PoolObjectFactory factory) {
0128: if (initialized)
0129: throw new IllegalStateException(INITIALIZED);
0130: this .factory = factory;
0131: }
0132:
0133: /**
0134: * Sets the object factory as a new factory for Java Beans. New instances
0135: * for the pool will be created with the no-argument constructor, and no
0136: * particular initialization or cleanup will be performed on the
0137: * instances.
0138: * @throws java.lang.IllegalStateException
0139: * Occurs when you try to set the object factory after the pool has been
0140: * initialized.
0141: */
0142: public void setObjectFactory(Class javaBeanClass) {
0143: if (initialized)
0144: throw new IllegalStateException(INITIALIZED);
0145: factory = new BeanFactory(javaBeanClass);
0146: }
0147:
0148: /**
0149: * Sets the name of the pool. This is not required to be unique across all
0150: * pools, but is strongly recommended. Certain uses of the pool (such as
0151: * a JNDI object factory) may require it. This must be set exactly once
0152: * for each pool (it may be set in the constructor).
0153: * @throws java.lang.IllegalStateException
0154: * Occurs when you try to set the name of the pool more than once.
0155: */
0156: public void setName(String name) {
0157: if (name == null || name.length() == 0)
0158: throw new IllegalArgumentException(
0159: "Cannot set pool name to null or empty!");
0160: if (poolName != null && !poolName.equals(name))
0161: throw new IllegalStateException(
0162: "Cannot change pool name once set!");
0163: poolName = name;
0164: log = Logger.getLogger(ObjectPool.class.getName() + "." + name);
0165: }
0166:
0167: /**
0168: * Gets the name of the pool.
0169: */
0170: public String getName() {
0171: return poolName;
0172: }
0173:
0174: /**
0175: * Sets the minimum size of the pool. The pool will create this many
0176: * instances at startup, and once running, it will never shrink below this
0177: * size. The default is zero.
0178: * @throws java.lang.IllegalStateException
0179: * Occurs when you try to set the minimum size after the pool has been
0180: * initialized.
0181: */
0182: public void setMinSize(int size) {
0183: if (initialized)
0184: throw new IllegalStateException(INITIALIZED);
0185: minSize = size;
0186: if (maxSize != 0 && minSize > maxSize) {
0187: maxSize = minSize;
0188: log.warn("pool max size set to " + maxSize
0189: + " to stay >= min size");
0190: }
0191: }
0192:
0193: /**
0194: * Gets the minimum size of the pool.
0195: * @see #setMinSize
0196: */
0197: public int getMinSize() {
0198: return minSize;
0199: }
0200:
0201: /**
0202: * Sets the maximum size of the pool. Once the pool has grown to hold this
0203: * number of instances, it will not add any more instances. If one of the
0204: * pooled instances is available when a request comes in, it will be
0205: * returned. If none of the pooled instances are available, the pool will
0206: * either block until an instance is available, or return null. The default
0207: * is no maximum size.
0208: * @see #setBlocking
0209: * @param size The maximum size of the pool, or 0 if the pool should grow
0210: * indefinitely (not recommended).
0211: * @throws java.lang.IllegalStateException
0212: * Occurs when you try to set the maximum size after the pool has been
0213: * initialized.
0214: */
0215: public void setMaxSize(int size) {
0216: if (initialized)
0217: throw new IllegalStateException(INITIALIZED);
0218: maxSize = size;
0219: if (maxSize != 0 && minSize > maxSize) {
0220: minSize = maxSize;
0221: log.warn("pool min size set to " + minSize
0222: + " to stay <= max size");
0223: }
0224: }
0225:
0226: /**
0227: * Gets the maximum size of the pool.
0228: * @see #setMaxSize
0229: */
0230: public int getMaxSize() {
0231: return maxSize;
0232: }
0233:
0234: /**
0235: * Sets whether the pool should release instances that have not been used
0236: * recently. This is intended to reclaim resources (memory, database
0237: * connections, file handles, etc) during periods of inactivity. This runs
0238: * as often as garbage collection (even if garbage collection is disabled,
0239: * this uses the same timing parameter), but the required period of
0240: * inactivity is different. All objects that have been unused for more
0241: * than the idle timeout are closed, but if you set the MaxIdleShrinkPercent
0242: * parameter, the pool may recreate some objects so the total number of
0243: * pooled instances doesn't shrink as rapidly. Also, under no circumstances
0244: * will the number of pooled instances fall below the minimum size.</p>
0245: * <P>The default is disabled.</P>
0246: * @see #setGCInterval
0247: * @see #setIdleTimeout
0248: * @see #setMaxIdleTimeoutPercent
0249: * @see #setMinSize
0250: * @throws java.lang.IllegalStateException
0251: * Occurs when you try to set the idle timeout state after the pool has
0252: * been initialized.
0253: */
0254: public void setIdleTimeoutEnabled(boolean enableTimeout) {
0255: if (initialized)
0256: throw new IllegalStateException(INITIALIZED);
0257: idleTimeout = enableTimeout;
0258: }
0259:
0260: /**
0261: * Gets whether the pool releases instances that have not been used
0262: * recently. This is different than garbage collection, which returns
0263: * instances to the pool if a client checked an instance out but has not
0264: * used it and not returned it to the pool.
0265: * @see #setIdleTimeoutEnabled
0266: */
0267: public boolean isIdleTimeoutEnabled() {
0268: return idleTimeout;
0269: }
0270:
0271: /**
0272: * Sets whether garbage collection is enabled. This is the process of
0273: * returning objects to the pool if they have been checked out of the pool
0274: * but have not been used in a long periond of time. This is meant to
0275: * reclaim resources, generally caused by unexpected failures on the part
0276: * of the pool client (which forestalled returning an object to the pool).
0277: * This runs on the same schedule as the idle timeout (if enabled), but
0278: * objects that were just garbage collected will not be eligible for the
0279: * idle timeout immediately (after all, they presumably represented "active"
0280: * clients). Objects that are garbage collected will be checked out again
0281: * immediately if a client is blocking waiting for an object. The default
0282: * value is disabled.
0283: * @see #setGCMinIdleTime
0284: * @see #setGCInterval
0285: * @throws java.lang.IllegalStateException
0286: * Occurs when you try to set the garbage collection state after the pool
0287: * has been initialized.
0288: */
0289: public void setGCEnabled(boolean enabled) {
0290: if (initialized)
0291: throw new IllegalStateException(INITIALIZED);
0292: runGC = enabled;
0293: }
0294:
0295: /**
0296: * Gets whether garbage collection is enabled.
0297: * @see #setGCEnabled
0298: */
0299: public boolean isGCEnabled() {
0300: return runGC;
0301: }
0302:
0303: /**
0304: * Sets the idle timeout percent as a fraction between 0 and 1. If a number
0305: * of objects are determined to be idle, they will all be closed and
0306: * removed from the pool. However, if the ratio of objects released to
0307: * objects in the pool is greater than this fraction, some new objects
0308: * will be created to replace the closed objects. This prevents the pool
0309: * size from decreasing too rapidly. Set to 0 to decrease the pool size by
0310: * a maximum of 1 object per test, or 1 to never replace objects that have
0311: * exceeded the idle timeout. The pool will always replace enough closed
0312: * connections to stay at the minimum size.
0313: * @see #setIdleTimeoutEnabled
0314: * @throws java.lang.IllegalStateException
0315: * Occurs when you try to set the idle timeout percent after the pool
0316: * has been initialized.
0317: * @throws java.lang.IllegalArgumentException
0318: * Occurs when the percent parameter is not between 0 and 1.
0319: */
0320: public void setMaxIdleTimeoutPercent(float percent) {
0321: if (initialized)
0322: throw new IllegalStateException(INITIALIZED);
0323: if (percent < 0f || percent > 1f)
0324: throw new IllegalArgumentException(
0325: "Percent must be between 0 and 1!");
0326: maxIdleShrinkPercent = percent;
0327: }
0328:
0329: /**
0330: * Gets the idle timeout percent as a fraction between 0 and 1.
0331: * @see #setMaxIdleTimeoutPercent
0332: */
0333: public float getMaxIdleTimeoutPercent() {
0334: return maxIdleShrinkPercent;
0335: }
0336:
0337: /**
0338: * Sets the minimum idle time to release an unused object from the pool. If
0339: * the object is not in use and has not been used for this amount of time,
0340: * it will be released from the pool. If timestamps are enabled, the client
0341: * may update the last used time. Otherwise, the last used time is only
0342: * updated when an object is acquired or released. The default value is
0343: * 30 minutes.
0344: * @see #setIdleTimeoutEnabled
0345: * @param millis The idle time, in milliseconds.
0346: * @throws java.lang.IllegalStateException
0347: * Occurs when you try to set the idle timeout after the pool
0348: * has been initialized.
0349: */
0350: public void setIdleTimeout(long millis) {
0351: if (initialized)
0352: throw new IllegalStateException(INITIALIZED);
0353: idleTimeoutMillis = millis;
0354:
0355: if (log.isDebugEnabled())
0356: log.debug("setIdleTimeout(" + millis + ")");
0357: }
0358:
0359: /**
0360: * Gets the minimum idle time to release an unused object from the pool.
0361: * @see #setIdleTimeout
0362: */
0363: public long getIdleTimeout() {
0364: return idleTimeoutMillis;
0365: }
0366:
0367: /**
0368: * Sets the minimum idle time to make an object eligible for garbage
0369: * collection. If the object is in use and has not been used for this
0370: * amount of time, it may be returned to the pool. If timestamps are
0371: * enabled, the client may update the last used time (this is generally
0372: * recommended if garbage collection is enabled). Otherwise, the last used
0373: * time is only updated when an object is acquired or released. The default
0374: * value is 20 minutes.
0375: * @see #setGCEnabled
0376: * @param millis The idle time, in milliseconds.
0377: * @throws java.lang.IllegalStateException
0378: * Occurs when you try to set the garbage collection idle time after the
0379: * pool has been initialized.
0380: */
0381: public void setGCMinIdleTime(long millis) {
0382: if (initialized)
0383: throw new IllegalStateException(INITIALIZED);
0384: gcMinIdleMillis = millis;
0385:
0386: if (log.isDebugEnabled())
0387: log.debug("setGCMinIdleTime(" + millis + ")");
0388: }
0389:
0390: /**
0391: * Gets the minimum idle time to make an object eligible for garbage
0392: * collection.
0393: * @see #setGCMinIdleTime
0394: */
0395: public long getGCMinIdleTime() {
0396: return gcMinIdleMillis;
0397: }
0398:
0399: /**
0400: * Sets the length of time between garbage collection and idle timeout runs.
0401: * This is inexact - if there are many pools with garbage collection and/or
0402: * the idle timeout enabled, there will not be a thread for each one, and
0403: * several nearby actions may be combined. Likewise if the collection
0404: * process is lengthy for certain types of pooled objects (not recommended),
0405: * other actions may be delayed. This is to prevend an unnecessary
0406: * proliferation of threads. Note that this parameter controls
0407: * both garbage collection and the idle timeout - and they will be performed
0408: * together if both are enabled. The deafult value is 2 minutes.
0409: * @throws java.lang.IllegalStateException
0410: * Occurs when you try to set the garbage collection interval after the
0411: * pool has been initialized.
0412: */
0413: public void setGCInterval(long millis) {
0414: if (initialized)
0415: throw new IllegalStateException(INITIALIZED);
0416:
0417: gcIntervalMillis = millis;
0418:
0419: if (log.isDebugEnabled())
0420: log.debug("setGCInterval(" + gcIntervalMillis + ")");
0421: }
0422:
0423: /**
0424: * Gets the length of time between garbage collection and idle timeout runs.
0425: * @see #setGCInterval
0426: */
0427: public long getGCInterval() {
0428: return gcIntervalMillis;
0429: }
0430:
0431: /**
0432: * Sets whether a request for an object will block if the pool size is
0433: * maxed out and no objects are available. If set to block, the request
0434: * will not return until an object is available. Otherwise, the request
0435: * will return null immediately (and may be retried). If multiple
0436: * requests block, there is no guarantee which will return first. The
0437: * default is to block.
0438: * @throws java.lang.IllegalStateException
0439: * Occurs when you try to set the blocking parameter after the
0440: * pool has been initialized.
0441: */
0442: public void setBlocking(boolean blocking) {
0443: if (initialized)
0444: throw new IllegalStateException(INITIALIZED);
0445: this .blocking = blocking;
0446: }
0447:
0448: /**
0449: * Gets whether a request for an object will block if the pool size is
0450: * maxed out and no objects are available.
0451: * @see #setBlocking
0452: */
0453: public boolean isBlocking() {
0454: return blocking;
0455: }
0456:
0457: /** sets how long to wait for a free object when blocking, -1 indicating
0458: * forever.
0459: */
0460: public void setBlockingTimeout(int blockingTimeout) {
0461: this .blockingTimeout = blockingTimeout;
0462: }
0463:
0464: /** get how long this pool will wait for a free object while blocking */
0465: public int getBlockingTimeout() {
0466: return this .blockingTimeout;
0467: }
0468:
0469: /**
0470: * Sets whether object clients can update the last used time. If not, the
0471: * last used time will only be updated when the object is given to a client
0472: * and returned to the pool. This time is important if the idle timeout or
0473: * garbage collection is enabled (particularly the latter). The default
0474: * is false.
0475: * @throws java.lang.IllegalStateException
0476: * Occurs when you try to set the timestamp parameter after the
0477: * pool has been initialized.
0478: */
0479: public void setTimestampUsed(boolean timestamp) {
0480: if (initialized)
0481: throw new IllegalStateException(INITIALIZED);
0482:
0483: trackLastUsed = timestamp;
0484:
0485: if (log.isDebugEnabled())
0486: log.debug("setTimestampUsed(" + timestamp + ")");
0487: }
0488:
0489: /**
0490: * Gets whether object clients can update the last used time.
0491: */
0492: public boolean isTimestampUsed() {
0493: return trackLastUsed;
0494: }
0495:
0496: /**
0497: * Sets the response for object errors. If this flag is set and an error
0498: * event occurs, the object is removed from the pool entirely. Otherwise,
0499: * the object is returned to the pool of available objects. For example, a
0500: * SQL error may not indicate a bad database connection (flag not set),
0501: * while a TCP/IP error probably indicates a bad network connection (flag
0502: * set). If this flag is not set, you can still manually invalidate
0503: * objects using markObjectAsInvalid.
0504: * @see #markObjectAsInvalid
0505: * @see #objectError
0506: */
0507: public void setInvalidateOnError(boolean invalidate) {
0508: if (initialized)
0509: throw new IllegalStateException(INITIALIZED);
0510: invalidateOnError = invalidate;
0511: }
0512:
0513: /**
0514: * Gets whether objects are removed from the pool in case of errors.
0515: */
0516: public boolean isInvalidateOnError() {
0517: return invalidateOnError;
0518: }
0519:
0520: /**
0521: * Prepares the pool for use. This must be called exactly once before
0522: * getObject is even called. The pool name and object factory must be set
0523: * before this call will succeed.
0524: * @throws java.lang.IllegalStateException
0525: * Occurs when you try to initialize the pool without setting the object
0526: * factory or name, or you initialize the pool more than once.
0527: */
0528: public void initialize() {
0529: if (factory == null || poolName == null)
0530: throw new IllegalStateException(
0531: "Factory and Name must be set before pool initialization!");
0532: if (initialized)
0533: throw new IllegalStateException(
0534: "Cannot initialize more than once!");
0535: initialized = true;
0536: permits = new FIFOSemaphore(maxSize);
0537: factory.poolStarted(this );
0538: lastGC = System.currentTimeMillis();
0539: //fill pool to min size
0540: fillToMin();
0541: /*
0542: int max = maxSize <= 0 ? minSize : Math.min(minSize, maxSize);
0543: Collection cs = new LinkedList();
0544: for(int i=0; i<max; i++)
0545: {
0546: cs.add(getObject(null));
0547: }
0548: while (Iterator i = cs.iterator(); i.hasNext();)
0549: {
0550: releaseObject(i.next());
0551: } // end of while ()
0552: */
0553: collector.addPool(this );
0554: }
0555:
0556: /**
0557: * Shuts down the pool. All outstanding objects are closed and all objects
0558: * are released from the pool. No getObject or releaseObject calls will
0559: * succeed after this method is called - and they will probably fail during
0560: * this method call.
0561: */
0562: public void shutDown() {
0563: collector.removePool(this );
0564: factory.poolClosing(this );
0565:
0566: // close all objects
0567: synchronized (objects) {
0568: for (Iterator it = objects.values().iterator(); it
0569: .hasNext();) {
0570: ObjectRecord rec = (ObjectRecord) it.next();
0571: if (null != rec) {
0572: if (rec.isInUse())
0573: factory.returnObject(rec.getClientObject());
0574: factory.deleteObject(rec.getObject());
0575: rec.close();
0576: }
0577: }
0578: objects.clear();
0579: deadObjects.clear();
0580: }//end of synch
0581:
0582: factory = null;
0583: poolName = null;
0584: initialized = false;
0585: }
0586:
0587: /**
0588: * Gets an object from the pool. If all the objects in the pool are in use,
0589: * creates a new object, adds it to the pool, and returns it. If all
0590: * objects are in use and the pool is at maximum size, will block or
0591: * return null.
0592: * @see #setBlocking
0593: */
0594: public Object getObject() {
0595: return getObject(null);
0596: }
0597:
0598: /**
0599: * Gets an object that fits the specified parameters from the pool.
0600: * If all the objects in the pool are in use or don't fit, creates
0601: * a new object, adds it to the pool, and returns it. If all
0602: * objects are in use or don't fit and the pool is at maximum size,
0603: * will block or return null.
0604: * @see #setBlocking
0605: */
0606: public Object getObject(Object parameters) {
0607: if (objects == null)
0608: throw new IllegalStateException(
0609: "Tried to use pool before it was Initialized or after it was ShutDown!");
0610:
0611: Object result = factory.isUniqueRequest();
0612: if (result != null) // If this is identical to a previous request,
0613: return result; // return the same result. This is the 2.4 total hack to
0614: //share local connections within a managed tx.
0615:
0616: try {
0617: if (permits.attempt(blockingTimeout)) {
0618: ObjectRecord rec = null;
0619: synchronized (objects) {
0620: for (Iterator it = objects.values().iterator(); it
0621: .hasNext();) {
0622: rec = (ObjectRecord) it.next();
0623: if (null != rec
0624: && !rec.isInUse()
0625: && factory.checkValidObject(rec
0626: .getObject(), parameters)) {
0627: log.info("Handing out from pool object: "
0628: + rec.getObject());
0629: try {
0630: rec.setInUse(true);
0631: } catch (ConcurrentModificationException e) {
0632: log
0633: .info("Conflict trying to set rec. in use flag:"
0634: + rec.getObject());
0635: // That's OK, just go on and try another object
0636: continue;//shouldn't happen now.
0637: }
0638: break;
0639: }
0640: rec = null;//not found
0641: }
0642: }//synch on objects
0643:
0644: if (rec == null) {
0645: rec = createNewObject(parameters);
0646: } // end of if ()
0647: if (rec == null) {
0648: throw new RuntimeException(
0649: "Pool is broken, did not find or create an object");
0650: } // end of if ()
0651: Object ob = rec.getObject();
0652:
0653: result = factory.prepareObject(ob);
0654: if (result != ob)
0655: rec.setClientObject(result);
0656: if (result instanceof PooledObject)
0657: ((PooledObject) result).addPoolEventListener(this );
0658:
0659: log.debug("Pool " + this + " gave out object: "
0660: + result);
0661: return result;
0662: }//end of permits
0663: else {
0664: //we timed out
0665: throw new RuntimeException(
0666: "No ManagedConnections Available!");
0667: } // end of else
0668: }//try
0669: catch (RuntimeException e) {
0670: throw e;
0671: } // end of catch
0672: catch (InterruptedException ie) {
0673: log.info("Interrupted while requesting permit!",
0674: new Exception("stacktrace"));
0675: throw new RuntimeException(
0676: "Interrupted while requesting permit!");
0677: } // end of try-catch
0678: catch (Exception e) {
0679: log.info("problem getting connection from pool", e);
0680: throw new RuntimeException(
0681: "problem getting connection from pool "
0682: + e.getMessage());
0683: } // end of catch
0684: }
0685:
0686: /**
0687: * Sets the last used time for an object in the pool that is currently
0688: * in use. If the timestamp parameter is not set, this call does nothing.
0689: * Otherwise, the object is marked as last used at the current time.
0690: * @throws java.lang.IllegalArgumentException
0691: * Occurs when the object is not recognized by the factory or not
0692: * in the pool.
0693: * @throws java.lang.IllegalStateException
0694: * Occurs when the object is not currently in use.
0695: * @see #setTimestampUsed
0696: */
0697: public void setLastUsed(Object object) {
0698: if (!trackLastUsed)
0699: return;
0700: Object ob = null;
0701: try {
0702: ob = factory.translateObject(object);
0703: } catch (Exception e) {
0704: throw new IllegalArgumentException("Pool " + getName()
0705: + " does not recognize object for last used time: "
0706: + object);
0707: }
0708: ObjectRecord rec = ob == null ? null : (ObjectRecord) objects
0709: .get(ob);
0710: if (rec == null)
0711: throw new IllegalArgumentException("Pool " + getName()
0712: + " does not recognize object for last used time: "
0713: + object);
0714: if (rec.isInUse())
0715: rec.setLastUsed();
0716: else
0717: throw new IllegalStateException(
0718: "Cannot set last updated time for an object that's not in use!");
0719: }
0720:
0721: /**
0722: * Indicates that an object is no longer valid, and should be removed from
0723: * the pool entirely. This should be called before the object is returned
0724: * to the pool (specifically, before factory.returnObject returns), or else
0725: * the object may be given out again by the time this is called! Also, you
0726: * still need to actually return the object to the pool by calling
0727: * releaseObject, if you aren't calling this during that process already.
0728: * @param object The object to invalidate, which must be the exact object
0729: * returned by getObject
0730: */
0731: public void markObjectAsInvalid(Object object) {
0732: if (deadObjects == null)
0733: throw new IllegalStateException(
0734: "Tried to use pool before it was Initialized or after it was ShutDown!");
0735: deadObjects.add(object); //a synchronized set
0736:
0737: }
0738:
0739: /**
0740: * Returns an object to the pool. This must be the exact object that was
0741: * given out by getObject, and it must be returned to the same pool that
0742: * generated it. If other clients are blocked waiting on an object, the
0743: * object may be re-released immediately.
0744: * @throws java.lang.IllegalArgumentException
0745: * Occurs when the object is not in this pool.
0746: */
0747: public void releaseObject(Object object) {
0748:
0749: log.debug("Pool " + this + " object released: " + object);
0750:
0751: Object pooled = null;
0752: try {
0753: factory.returnObject(object);//do this first
0754: pooled = factory.translateObject(object);
0755: } catch (Exception e) {
0756: return; // We can't release it if the factory can't recognize it
0757: }
0758: if (pooled == null) // We can't release it if the factory can't recognize it
0759: return;
0760: boolean removed = false;
0761: synchronized (objects) {
0762: ObjectRecord rec = (ObjectRecord) objects.get(pooled);
0763: if (rec == null) // Factory understands it, but we don't
0764: throw new IllegalArgumentException("Object " + object
0765: + " is not in pool " + poolName + "!");
0766: if (!rec.isInUse())
0767: return; // Must have been released by GC?
0768: if (object instanceof PooledObject)
0769: ((PooledObject) object).removePoolEventListener(this );
0770: removed = deadObjects.remove(object);
0771: rec.setInUse(false);
0772: if (removed) {
0773: log.debug("Object was dead: " + object);
0774: objects.remove(pooled);
0775: rec.close();
0776: } // end of if ()
0777:
0778: }//end of synch on objects
0779: if (removed) {
0780: try {
0781: factory.deleteObject(pooled);
0782: } catch (Exception e) {
0783: log.error("Pool " + this + " factory ("
0784: + factory.getClass().getName()
0785: + " delete error: ", e);
0786: }
0787: fillToMin();
0788: /*FIXME --MINSIZE
0789: if(objects.size() < minSize)
0790: createNewObject(null);
0791: */
0792: }
0793:
0794: if (removed)
0795: log.debug("Pool " + this + " destroyed object " + object
0796: + ".");
0797: else
0798: log.debug("Pool " + this + " returned object " + object
0799: + " to the pool.");
0800:
0801: permits.release();
0802: /*
0803: if(blocking)
0804: {
0805: synchronized(this)
0806: {
0807: notify();
0808: }
0809: }
0810: */
0811: }
0812:
0813: private int getUsedCount() {
0814: int total = 0;
0815: synchronized (objects) {
0816: for (Iterator it = new HashSet(objects.values()).iterator(); it
0817: .hasNext();) {
0818: ObjectRecord or = (ObjectRecord) it.next();
0819: if (or != null && or.isInUse())
0820: ++total;
0821: }
0822: }
0823: return total;
0824: }
0825:
0826: /**
0827: * Returns the pool name and status.
0828: */
0829: public String toString() {
0830: return poolName
0831: + " ["
0832: + getUsedCount()
0833: + "/"
0834: + (objects == null ? 0 : objects.size())
0835: + "/"
0836: + (maxSize == 0 ? "Unlimited" : Integer
0837: .toString(maxSize)) + "]";
0838: }
0839:
0840: // ---- PoolEventListener Implementation ----
0841:
0842: /**
0843: * If the object has been closed, release it.
0844: */
0845: public void objectClosed(PoolEvent evt) {
0846: releaseObject(evt.getSource());
0847: }
0848:
0849: /**
0850: * If the invalidateOnError flag is set, the object will be removed from
0851: * the pool entirely when the client has finished with it.
0852: */
0853: public void objectError(PoolEvent evt) {
0854: if (invalidateOnError || evt.isCatastrophic())
0855: markObjectAsInvalid(evt.getSource());
0856: }
0857:
0858: /**
0859: * If we're tracking the last used times, update the last used time for the
0860: * specified object.
0861: */
0862: public void objectUsed(PoolEvent evt) {
0863: if (!trackLastUsed)
0864: return;
0865: setLastUsed(evt.getSource());
0866: }
0867:
0868: long getNextGCMillis(long now) {
0869: long t = lastGC + gcIntervalMillis - now;
0870:
0871: log.debug("getNextGCMillis(): returning " + t);
0872:
0873: if (!runGC)
0874: return Long.MAX_VALUE;
0875:
0876: return t;
0877: }
0878:
0879: // Allow GC if we're within 10% of the desired interval
0880: boolean isTimeToGC() {
0881: long now;
0882: now = System.currentTimeMillis();
0883:
0884: log.debug("isTimeToGC(): "
0885: + (now >= lastGC
0886: + Math.round((float) gcIntervalMillis * 0.9f)));
0887:
0888: return now >= lastGC
0889: + Math.round((float) gcIntervalMillis * 0.9f);
0890:
0891: }
0892:
0893: void runGCandShrink() {
0894:
0895: log.debug("runGCandShrink(): runGC = " + runGC
0896: + "; idleTimeout = " + idleTimeout);
0897:
0898: if (runGC || idleTimeout) {
0899: HashSet objsCopy;
0900: synchronized (objects) {
0901: objsCopy = new HashSet(objects.values());
0902: }
0903:
0904: if (runGC) { // Garbage collection - return any object that's been out too long with no use
0905: Iterator it = objsCopy.iterator();
0906: while (it.hasNext()) {
0907: ObjectRecord rec = (ObjectRecord) it.next();
0908: if (rec != null
0909: && rec.isInUse()
0910: && rec.getMillisSinceLastUse() >= gcMinIdleMillis) {
0911: releaseObject(rec.getClientObject());
0912: }
0913: }
0914: }
0915: if (idleTimeout) { // Shrinking the pool - remove objects from the pool if they have not been used in a long time
0916: // Find object eligible for removal
0917: HashSet eligible = new HashSet();
0918: Iterator it = objsCopy.iterator();
0919: while (it.hasNext()) {
0920: ObjectRecord rec = (ObjectRecord) it.next();
0921: if (rec != null
0922: && !rec.isInUse()
0923: && rec.getMillisSinceLastUse() > idleTimeoutMillis)
0924: eligible.add(rec);
0925: }
0926: // Calculate max number of objects to remove without replacing
0927: int max = Math.round(eligible.size()
0928: * maxIdleShrinkPercent);
0929: if (max == 0 && eligible.size() > 0)
0930: max = 1;
0931: int count = 0;
0932: // Attempt to remove that many objects
0933: it = eligible.iterator();
0934: while (it.hasNext()) {
0935: try {
0936: // Delete the object
0937: ObjectRecord rec = (ObjectRecord) it.next();
0938: if (rec != null) {
0939: rec.setInUse(true); // Don't let someone use it while we destroy it
0940: Object pooled = rec.getObject();
0941: synchronized (objects) {
0942: objects.remove(pooled);
0943: }
0944: //removeObject(pooled);
0945: try {
0946: factory.deleteObject(pooled);
0947: } catch (Exception e) {
0948: log.error("Pool " + this + " factory ("
0949: + factory.getClass().getName()
0950: + " delete error: ", e);
0951: }
0952: rec.close();
0953: ++count;
0954: }
0955: fillToMin();
0956: /*FIXME --MINSIZE
0957: if(count > max || objects.size() < minSize)
0958: createNewObject(null);
0959:
0960: */
0961: } catch (ConcurrentModificationException e) {
0962: }
0963: }
0964: }
0965: }
0966: lastGC = System.currentTimeMillis();
0967: }
0968:
0969: /**
0970: * Removes an object from the pool. Only one thread can add or remove
0971: * an object at a time.
0972: */
0973: /* private void removeObject(Object pooled)
0974: {
0975: synchronized(objects)
0976: {
0977: objects.remove(pooled);
0978: }
0979: }
0980: */
0981: /**
0982: * Creates a new Object.
0983: * @param parameters If <b>true</b>, then the object is locked and
0984: * translated by the factory, and the resulting object
0985: * returned. If <b>false</b>, then the object is left in the
0986: * pool unlocked.
0987: */
0988: private ObjectRecord createNewObject(Object parameters) {
0989: Object ob = null;
0990: try {
0991: ob = factory.createObject(parameters);
0992: } catch (Exception ex) {
0993: throw new RuntimeException("Could not create connection");
0994: }
0995: if (ob != null) { // if factory can create object
0996: ObjectRecord rec = new ObjectRecord(ob);
0997: synchronized (objects) {
0998: objects.put(ob, rec);
0999: }
1000: return rec;
1001: } else {
1002: throw new RuntimeException("could not create new object!");
1003: }
1004: }
1005:
1006: public void fillToMin() {
1007: Collection newMCs = new ArrayList();
1008: try {
1009: while (objects.size() < minSize) {
1010: newMCs.add(getObject(null));
1011: } // end of while ()
1012: } catch (Exception re) {
1013: //Whatever the reason, stop trying to add more!
1014: } // end of try-catch
1015: for (Iterator i = newMCs.iterator(); i.hasNext();) {
1016: releaseObject(i.next());
1017: } // end of for ()
1018:
1019: }
1020:
1021: }
1022:
1023: class BeanFactory extends PoolObjectFactory {
1024:
1025: private Class beanClass;
1026:
1027: private Logger log = Logger.getLogger(BeanFactory.class);
1028:
1029: public BeanFactory(Class beanClass) {
1030: try {
1031: beanClass.getConstructor(new Class[0]);
1032: } catch (NoSuchMethodException e) {
1033: throw new IllegalArgumentException(
1034: "Bean class doesn't have no-arg constructor!");
1035: }
1036: this .beanClass = beanClass;
1037: }
1038:
1039: public void poolStarted(ObjectPool pool) {
1040: super .poolStarted(pool);
1041: }
1042:
1043: public Object createObject(Object parameters) {
1044: try {
1045: return beanClass.newInstance();
1046: } catch (Exception e) {
1047: log.error("Unable to create instance of "
1048: + beanClass.getName(), e);
1049: }
1050: return null;
1051: }
1052: }
|