KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > minerva > pool > ObjectPool


1 /*
2  * Licensed under the X license (see http://www.x.org/terms.htm)
3  */

4 package org.ofbiz.minerva.pool;
5
6
7 import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
8
9 import java.util.ArrayList JavaDoc;
10 import java.util.Collection JavaDoc;
11 import java.util.Collections JavaDoc;
12 import java.util.ConcurrentModificationException JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import org.apache.log4j.Logger;
20
21 /**
22  * A generic object pool. You must provide a PoolObjectFactory (or the class
23  * of a Java Bean) so the pool knows what kind of objects to create. It has
24  * many configurable parameters, such as the minimum and maximum size of the
25  * pool, whether to enable idle timeouts, etc. If the pooled objects
26  * implement PooledObject, they will automatically be returned to the pool at
27  * the appropriate times.
28  * <P>In general, the appropriate way to use a pool is:</P>
29  * <OL>
30  * <LI>Create it</LI>
31  * <LI>Configure it (set factory, name, parameters, etc.)</LI>
32  * <LI>Initialize it (once done, further configuration is not allowed)</LI>
33  * <LI>Use it</LI>
34  * <LI>Shut it down</LI>
35  * </OL>
36  * @see org.ofbiz.minerva.pool.PooledObject
37  *
38  * @author Aaron Mulder (ammulder@alumni.princeton.edu)
39  * @author <a HREF="mailto:danch@nvisia.com">danch (Dan Christopherson)</a>
40  *
41  * Revision:
42  * 20010701 danch added code for timeout in blocking.
43  */

44 public class ObjectPool implements PoolEventListener {
45
46     private final static String JavaDoc 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 JavaDoc poolName;
56
57     private final Map JavaDoc objects = new HashMap JavaDoc();
58     private final Set JavaDoc deadObjects = Collections.synchronizedSet(new HashSet JavaDoc());
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; // don't replace idle connections that timeout
64
private long idleTimeoutMillis = 1800000l; // must be idle in pool for 30 minutes
65
private long gcMinIdleMillis = 1200000l; // must be unused by client for 20 minutes
66
private long gcIntervalMillis = 120000l; // shrink & gc every 2 minutes
67
private long lastGC = System.currentTimeMillis();
68     private boolean blocking = true;
69     private int blockingTimeout = 10000;//Integer.MAX_VALUE;//this is silly
70
private boolean trackLastUsed = false;
71     private boolean invalidateOnError = false;
72
73     private FIFOSemaphore permits;
74     private boolean initialized = false;
75
76     /**
77      * Creates a new pool. It cannot be used until you specify a name and
78      * object factory or bean class, and initialize it.
79      * @see #setName
80      * @see #setObjectFactory
81      * @see #initialize
82      */

83     public ObjectPool() {
84     }
85
86     /**
87      * Creates a new pool with the specified parameters. It cannot be used
88      * until you initialize it.
89      * @param factory The object factory that will create the objects to go in
90      * the pool.
91      * @param poolName The name of the pool. This does not have to be unique
92      * across all pools, but it is strongly recommended (and it may be a
93      * requirement for certain uses of the pool).
94      * @see #initialize
95      */

96     public ObjectPool(PoolObjectFactory factory, String JavaDoc poolName) {
97         setObjectFactory(factory);
98         setName(poolName);
99     }
100
101     /**
102      * Creates a new pool with the specified parameters. It cannot be used
103      * until you initialize it.
104      * @param javaBeanClass The Class of a Java Bean. New instances for the
105      * pool will be created with the no-argument constructor, and no
106      * particular initialization or cleanup will be performed on the
107      * instances. Use a PoolObjectFactory if you want more control over
108      * the instances.
109      * @param poolName The name of the pool. This does not have to be unique
110      * across all pools, but it is strongly recommended (and it may be a
111      * requirement for certain uses of the pool).
112      * @see #initialize
113      */

114     public ObjectPool(Class JavaDoc javaBeanClass, String JavaDoc poolName) {
115         setObjectFactory(javaBeanClass);
116         setName(poolName);
117     }
118
119     /**
120      * Sets the object factory for the pool. The object factory controls the
121      * instances created for the pool, and can initialize instances given out
122      * by the pool and cleanup instances returned to the pool.
123      * @throws java.lang.IllegalStateException
124      * Occurs when you try to set the object factory after the pool has been
125      * initialized.
126      */

127     public void setObjectFactory(PoolObjectFactory factory) {
128         if (initialized)
129             throw new IllegalStateException JavaDoc(INITIALIZED);
130         this.factory = factory;
131     }
132
133     /**
134      * Sets the object factory as a new factory for Java Beans. New instances
135      * for the pool will be created with the no-argument constructor, and no
136      * particular initialization or cleanup will be performed on the
137      * instances.
138      * @throws java.lang.IllegalStateException
139      * Occurs when you try to set the object factory after the pool has been
140      * initialized.
141      */

142     public void setObjectFactory(Class JavaDoc javaBeanClass) {
143         if (initialized)
144             throw new IllegalStateException JavaDoc(INITIALIZED);
145         factory = new BeanFactory(javaBeanClass);
146     }
147
148     /**
149      * Sets the name of the pool. This is not required to be unique across all
150      * pools, but is strongly recommended. Certain uses of the pool (such as
151      * a JNDI object factory) may require it. This must be set exactly once
152      * for each pool (it may be set in the constructor).
153      * @throws java.lang.IllegalStateException
154      * Occurs when you try to set the name of the pool more than once.
155      */

156     public void setName(String JavaDoc name) {
157         if (name == null || name.length() == 0)
158             throw new IllegalArgumentException JavaDoc("Cannot set pool name to null or empty!");
159         if (poolName != null && !poolName.equals(name))
160             throw new IllegalStateException JavaDoc("Cannot change pool name once set!");
161         poolName = name;
162         log = Logger.getLogger(ObjectPool.class.getName() + "." + name);
163     }
164
165     /**
166      * Gets the name of the pool.
167      */

168     public String JavaDoc getName() {
169         return poolName;
170     }
171
172     /**
173      * Sets the minimum size of the pool. The pool will create this many
174      * instances at startup, and once running, it will never shrink below this
175      * size. The default is zero.
176      * @throws java.lang.IllegalStateException
177      * Occurs when you try to set the minimum size after the pool has been
178      * initialized.
179      */

180     public void setMinSize(int size) {
181         if (initialized)
182             throw new IllegalStateException JavaDoc(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     /**
191      * Gets the minimum size of the pool.
192      * @see #setMinSize
193      */

194     public int getMinSize() {
195         return minSize;
196     }
197
198     /**
199      * Sets the maximum size of the pool. Once the pool has grown to hold this
200      * number of instances, it will not add any more instances. If one of the
201      * pooled instances is available when a request comes in, it will be
202      * returned. If none of the pooled instances are available, the pool will
203      * either block until an instance is available, or return null. The default
204      * is no maximum size.
205      * @see #setBlocking
206      * @param size The maximum size of the pool, or 0 if the pool should grow
207      * indefinitely (not recommended).
208      * @throws java.lang.IllegalStateException
209      * Occurs when you try to set the maximum size after the pool has been
210      * initialized.
211      */

212     public void setMaxSize(int size) {
213         if (initialized)
214             throw new IllegalStateException JavaDoc(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     /**
223      * Gets the maximum size of the pool.
224      * @see #setMaxSize
225      */

226     public int getMaxSize() {
227         return maxSize;
228     }
229
230     /**
231      * Sets whether the pool should release instances that have not been used
232      * recently. This is intended to reclaim resources (memory, database
233      * connections, file handles, etc) during periods of inactivity. This runs
234      * as often as garbage collection (even if garbage collection is disabled,
235      * this uses the same timing parameter), but the required period of
236      * inactivity is different. All objects that have been unused for more
237      * than the idle timeout are closed, but if you set the MaxIdleShrinkPercent
238      * parameter, the pool may recreate some objects so the total number of
239      * pooled instances doesn't shrink as rapidly. Also, under no circumstances
240      * will the number of pooled instances fall below the minimum size.</p>
241      * <P>The default is disabled.</P>
242      * @see #setGCInterval
243      * @see #setIdleTimeout
244      * @see #setMaxIdleTimeoutPercent
245      * @see #setMinSize
246      * @throws java.lang.IllegalStateException
247      * Occurs when you try to set the idle timeout state after the pool has
248      * been initialized.
249      */

250     public void setIdleTimeoutEnabled(boolean enableTimeout) {
251         if (initialized)
252             throw new IllegalStateException JavaDoc(INITIALIZED);
253         idleTimeout = enableTimeout;
254     }
255
256     /**
257      * Gets whether the pool releases instances that have not been used
258      * recently. This is different than garbage collection, which returns
259      * instances to the pool if a client checked an instance out but has not
260      * used it and not returned it to the pool.
261      * @see #setIdleTimeoutEnabled
262      */

263     public boolean isIdleTimeoutEnabled() {
264         return idleTimeout;
265     }
266
267     /**
268      * Sets whether garbage collection is enabled. This is the process of
269      * returning objects to the pool if they have been checked out of the pool
270      * but have not been used in a long periond of time. This is meant to
271      * reclaim resources, generally caused by unexpected failures on the part
272      * of the pool client (which forestalled returning an object to the pool).
273      * This runs on the same schedule as the idle timeout (if enabled), but
274      * objects that were just garbage collected will not be eligible for the
275      * idle timeout immediately (after all, they presumably represented "active"
276      * clients). Objects that are garbage collected will be checked out again
277      * immediately if a client is blocking waiting for an object. The default
278      * value is disabled.
279      * @see #setGCMinIdleTime
280      * @see #setGCInterval
281      * @throws java.lang.IllegalStateException
282      * Occurs when you try to set the garbage collection state after the pool
283      * has been initialized.
284      */

285     public void setGCEnabled(boolean enabled) {
286         if (initialized)
287             throw new IllegalStateException JavaDoc(INITIALIZED);
288         runGC = enabled;
289     }
290
291     /**
292      * Gets whether garbage collection is enabled.
293      * @see #setGCEnabled
294      */

295     public boolean isGCEnabled() {
296         return runGC;
297     }
298
299     /**
300      * Sets the idle timeout percent as a fraction between 0 and 1. If a number
301      * of objects are determined to be idle, they will all be closed and
302      * removed from the pool. However, if the ratio of objects released to
303      * objects in the pool is greater than this fraction, some new objects
304      * will be created to replace the closed objects. This prevents the pool
305      * size from decreasing too rapidly. Set to 0 to decrease the pool size by
306      * a maximum of 1 object per test, or 1 to never replace objects that have
307      * exceeded the idle timeout. The pool will always replace enough closed
308      * connections to stay at the minimum size.
309      * @see #setIdleTimeoutEnabled
310      * @throws java.lang.IllegalStateException
311      * Occurs when you try to set the idle timeout percent after the pool
312      * has been initialized.
313      * @throws java.lang.IllegalArgumentException
314      * Occurs when the percent parameter is not between 0 and 1.
315      */

316     public void setMaxIdleTimeoutPercent(float percent) {
317         if (initialized)
318             throw new IllegalStateException JavaDoc(INITIALIZED);
319         if (percent < 0f || percent > 1f)
320             throw new IllegalArgumentException JavaDoc("Percent must be between 0 and 1!");
321         maxIdleShrinkPercent = percent;
322     }
323
324     /**
325      * Gets the idle timeout percent as a fraction between 0 and 1.
326      * @see #setMaxIdleTimeoutPercent
327      */

328     public float getMaxIdleTimeoutPercent() {
329         return maxIdleShrinkPercent;
330     }
331
332     /**
333      * Sets the minimum idle time to release an unused object from the pool. If
334      * the object is not in use and has not been used for this amount of time,
335      * it will be released from the pool. If timestamps are enabled, the client
336      * may update the last used time. Otherwise, the last used time is only
337      * updated when an object is acquired or released. The default value is
338      * 30 minutes.
339      * @see #setIdleTimeoutEnabled
340      * @param millis The idle time, in milliseconds.
341      * @throws java.lang.IllegalStateException
342      * Occurs when you try to set the idle timeout after the pool
343      * has been initialized.
344      */

345     public void setIdleTimeout(long millis) {
346         if (initialized)
347             throw new IllegalStateException JavaDoc(INITIALIZED);
348         idleTimeoutMillis = millis;
349
350         if (log.isDebugEnabled())
351             log.debug("setIdleTimeout(" + millis + ")");
352     }
353
354     /**
355      * Gets the minimum idle time to release an unused object from the pool.
356      * @see #setIdleTimeout
357      */

358     public long getIdleTimeout() {
359         return idleTimeoutMillis;
360     }
361
362     /**
363      * Sets the minimum idle time to make an object eligible for garbage
364      * collection. If the object is in use and has not been used for this
365      * amount of time, it may be returned to the pool. If timestamps are
366      * enabled, the client may update the last used time (this is generally
367      * recommended if garbage collection is enabled). Otherwise, the last used
368      * time is only updated when an object is acquired or released. The default
369      * value is 20 minutes.
370      * @see #setGCEnabled
371      * @param millis The idle time, in milliseconds.
372      * @throws java.lang.IllegalStateException
373      * Occurs when you try to set the garbage collection idle time after the
374      * pool has been initialized.
375      */

376     public void setGCMinIdleTime(long millis) {
377         if (initialized)
378             throw new IllegalStateException JavaDoc(INITIALIZED);
379         gcMinIdleMillis = millis;
380
381         if (log.isDebugEnabled())
382             log.debug("setGCMinIdleTime(" + millis + ")");
383     }
384
385     /**
386      * Gets the minimum idle time to make an object eligible for garbage
387      * collection.
388      * @see #setGCMinIdleTime
389      */

390     public long getGCMinIdleTime() {
391         return gcMinIdleMillis;
392     }
393
394     /**
395      * Sets the length of time between garbage collection and idle timeout runs.
396      * This is inexact - if there are many pools with garbage collection and/or
397      * the idle timeout enabled, there will not be a thread for each one, and
398      * several nearby actions may be combined. Likewise if the collection
399      * process is lengthy for certain types of pooled objects (not recommended),
400      * other actions may be delayed. This is to prevend an unnecessary
401      * proliferation of threads. Note that this parameter controls
402      * both garbage collection and the idle timeout - and they will be performed
403      * together if both are enabled. The deafult value is 2 minutes.
404      * @throws java.lang.IllegalStateException
405      * Occurs when you try to set the garbage collection interval after the
406      * pool has been initialized.
407      */

408     public void setGCInterval(long millis) {
409         if (initialized)
410             throw new IllegalStateException JavaDoc(INITIALIZED);
411
412         gcIntervalMillis = millis;
413
414         if (log.isDebugEnabled())
415             log.debug("setGCInterval(" + gcIntervalMillis + ")");
416     }
417
418     /**
419      * Gets the length of time between garbage collection and idle timeout runs.
420      * @see #setGCInterval
421      */

422     public long getGCInterval() {
423         return gcIntervalMillis;
424     }
425
426     /**
427      * Sets whether a request for an object will block if the pool size is
428      * maxed out and no objects are available. If set to block, the request
429      * will not return until an object is available. Otherwise, the request
430      * will return null immediately (and may be retried). If multiple
431      * requests block, there is no guarantee which will return first. The
432      * default is to block.
433      * @throws java.lang.IllegalStateException
434      * Occurs when you try to set the blocking parameter after the
435      * pool has been initialized.
436      */

437     public void setBlocking(boolean blocking) {
438         if (initialized)
439             throw new IllegalStateException JavaDoc(INITIALIZED);
440         this.blocking = blocking;
441     }
442
443     /**
444      * Gets whether a request for an object will block if the pool size is
445      * maxed out and no objects are available.
446      * @see #setBlocking
447      */

448     public boolean isBlocking() {
449         return blocking;
450     }
451
452     /** sets how long to wait for a free object when blocking, -1 indicating
453      * forever.
454      */

455     public void setBlockingTimeout(int blockingTimeout) {
456         this.blockingTimeout = blockingTimeout;
457     }
458
459     /** get how long this pool will wait for a free object while blocking */
460     public int getBlockingTimeout() {
461         return this.blockingTimeout;
462     }
463
464     /**
465      * Sets whether object clients can update the last used time. If not, the
466      * last used time will only be updated when the object is given to a client
467      * and returned to the pool. This time is important if the idle timeout or
468      * garbage collection is enabled (particularly the latter). The default
469      * is false.
470      * @throws java.lang.IllegalStateException
471      * Occurs when you try to set the timestamp parameter after the
472      * pool has been initialized.
473      */

474     public void setTimestampUsed(boolean timestamp) {
475         if (initialized)
476             throw new IllegalStateException JavaDoc(INITIALIZED);
477
478         trackLastUsed = timestamp;
479
480         if (log.isDebugEnabled())
481             log.debug("setTimestampUsed(" + timestamp + ")");
482     }
483
484     /**
485      * Gets whether object clients can update the last used time.
486      */

487     public boolean isTimestampUsed() {
488         return trackLastUsed;
489     }
490
491     /**
492      * Sets the response for object errors. If this flag is set and an error
493      * event occurs, the object is removed from the pool entirely. Otherwise,
494      * the object is returned to the pool of available objects. For example, a
495      * SQL error may not indicate a bad database connection (flag not set),
496      * while a TCP/IP error probably indicates a bad network connection (flag
497      * set). If this flag is not set, you can still manually invalidate
498      * objects using markObjectAsInvalid.
499      * @see #markObjectAsInvalid
500      * @see #objectError
501      */

502     public void setInvalidateOnError(boolean invalidate) {
503         if (initialized)
504             throw new IllegalStateException JavaDoc(INITIALIZED);
505         invalidateOnError = invalidate;
506     }
507
508     /**
509      * Gets whether objects are removed from the pool in case of errors.
510      */

511     public boolean isInvalidateOnError() {
512         return invalidateOnError;
513     }
514
515     /**
516      * Prepares the pool for use. This must be called exactly once before
517      * getObject is even called. The pool name and object factory must be set
518      * before this call will succeed.
519      * @throws java.lang.IllegalStateException
520      * Occurs when you try to initialize the pool without setting the object
521      * factory or name, or you initialize the pool more than once.
522      */

523     public void initialize() {
524         if (factory == null || poolName == null)
525             throw new IllegalStateException JavaDoc("Factory and Name must be set before pool initialization!");
526         if (initialized)
527             throw new IllegalStateException JavaDoc("Cannot initialize more than once!");
528         initialized = true;
529         permits = new FIFOSemaphore(maxSize);
530         factory.poolStarted(this);
531         lastGC = System.currentTimeMillis();
532         //fill pool to min size
533
fillToMin();
534         /*
535         int max = maxSize <= 0 ? minSize : Math.min(minSize, maxSize);
536         Collection cs = new LinkedList();
537         for(int i=0; i<max; i++)
538         {
539            cs.add(getObject(null));
540         }
541         while (Iterator i = cs.iterator(); i.hasNext();)
542         {
543            releaseObject(i.next());
544         } // end of while ()
545         */

546         collector.addPool(this);
547     }
548
549     /**
550      * Shuts down the pool. All outstanding objects are closed and all objects
551      * are released from the pool. No getObject or releaseObject calls will
552      * succeed after this method is called - and they will probably fail during
553      * this method call.
554      */

555     public void shutDown() {
556         collector.removePool(this);
557         factory.poolClosing(this);
558
559         // close all objects
560
synchronized (objects) {
561             for (Iterator JavaDoc 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         }//end of synch
573

574         factory = null;
575         poolName = null;
576         initialized = false;
577     }
578
579     /**
580      * Gets an object from the pool. If all the objects in the pool are in use,
581      * creates a new object, adds it to the pool, and returns it. If all
582      * objects are in use and the pool is at maximum size, will block or
583      * return null.
584      * @see #setBlocking
585      */

586     public Object JavaDoc getObject() {
587         return getObject(null);
588     }
589
590     /**
591      * Gets an object that fits the specified parameters from the pool.
592      * If all the objects in the pool are in use or don't fit, creates
593      * a new object, adds it to the pool, and returns it. If all
594      * objects are in use or don't fit and the pool is at maximum size,
595      * will block or return null.
596      * @see #setBlocking
597      */

598     public Object JavaDoc getObject(Object JavaDoc parameters) {
599         if (objects == null)
600             throw new IllegalStateException JavaDoc("Tried to use pool before it was Initialized or after it was ShutDown!");
601
602         Object JavaDoc result = factory.isUniqueRequest();
603         if (result != null) // If this is identical to a previous request,
604
return result; // return the same result. This is the 2.4 total hack to
605
// share local connections within a managed tx.
606

607         try {
608             if (permits.attempt(blockingTimeout)) {
609                 ObjectRecord rec = null;
610                 synchronized (objects) {
611                     for (Iterator JavaDoc 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 JavaDoc e) {
618                                 log.info("Conflict trying to set rec. in use flag:" + rec.getObject());
619                                 // That's OK, just go on and try another object
620
continue;//shouldn't happen now.
621
}
622                             break;
623                         }
624                         rec = null;//not found
625
}
626                 }//synch on objects
627

628                 if (rec == null) {
629                     try {
630                         rec = createNewObject(parameters);
631                     } catch (Exception JavaDoc e) {
632                         log.error("Exception in creating new object for pool", e);
633                         permits.release();
634                         throw e;
635                     }
636                 } // end of if ()
637
if (rec == null) {
638                     permits.release();
639                     String JavaDoc message = "Pool is broken, did not find or create an object";
640                     log.error(message);
641                     throw new RuntimeException JavaDoc(message);
642                 } // end of if ()
643
Object JavaDoc 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             }//end of permits
653
else {
654                 //we timed out
655
throw new RuntimeException JavaDoc("No ManagedConnections Available!");
656             } // end of else
657
}//try
658
catch (RuntimeException JavaDoc e) {
659             throw e;
660         } // end of catch
661
catch (InterruptedException JavaDoc ie) {
662             log.info("Interrupted while requesting permit!", new Exception JavaDoc("stacktrace"));
663             throw new RuntimeException JavaDoc("Interrupted while requesting permit!");
664         } // end of try-catch
665
catch (Exception JavaDoc e) {
666             log.info("problem getting connection from pool", e);
667             throw new RuntimeException JavaDoc("problem getting connection from pool " + e.getMessage());
668         } // end of catch
669
}
670
671     /**
672      * Sets the last used time for an object in the pool that is currently
673      * in use. If the timestamp parameter is not set, this call does nothing.
674      * Otherwise, the object is marked as last used at the current time.
675      * @throws java.lang.IllegalArgumentException
676      * Occurs when the object is not recognized by the factory or not
677      * in the pool.
678      * @throws java.lang.IllegalStateException
679      * Occurs when the object is not currently in use.
680      * @see #setTimestampUsed
681      */

682     public void setLastUsed(Object JavaDoc object) {
683         if (!trackLastUsed) return;
684         Object JavaDoc ob = null;
685         try {
686             ob = factory.translateObject(object);
687         } catch (Exception JavaDoc e) {
688             throw new IllegalArgumentException JavaDoc("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 JavaDoc("Pool " + getName() + " does not recognize object for last used time: " + object);
693         if (rec.isInUse())
694             rec.setLastUsed();
695         else
696             throw new IllegalStateException JavaDoc("Cannot set last updated time for an object that's not in use!");
697     }
698
699     /**
700      * Indicates that an object is no longer valid, and should be removed from
701      * the pool entirely. This should be called before the object is returned
702      * to the pool (specifically, before factory.returnObject returns), or else
703      * the object may be given out again by the time this is called! Also, you
704      * still need to actually return the object to the pool by calling
705      * releaseObject, if you aren't calling this during that process already.
706      * @param object The object to invalidate, which must be the exact object
707      * returned by getObject
708      */

709     public void markObjectAsInvalid(Object JavaDoc object) {
710         if (deadObjects == null)
711             throw new IllegalStateException JavaDoc("Tried to use pool before it was Initialized or after it was ShutDown!");
712         deadObjects.add(object); //a synchronized set
713

714     }
715
716     /**
717      * Returns an object to the pool. This must be the exact object that was
718      * given out by getObject, and it must be returned to the same pool that
719      * generated it. If other clients are blocked waiting on an object, the
720      * object may be re-released immediately.
721      * @throws java.lang.IllegalArgumentException
722      * Occurs when the object is not in this pool.
723      */

724     public void releaseObject(Object JavaDoc object) {
725
726         log.debug("Pool " + this + " object released: " + object);
727
728         Object JavaDoc pooled = null;
729         try {
730             factory.returnObject(object);//do this first
731
pooled = factory.translateObject(object);
732         } catch (Exception JavaDoc e) {
733             return; // We can't release it if the factory can't recognize it
734
}
735         if (pooled == null) // We can't release it if the factory can't recognize it
736
return;
737         boolean removed = false;
738         synchronized (objects) {
739             ObjectRecord rec = (ObjectRecord) objects.get(pooled);
740             if (rec == null) // Factory understands it, but we don't
741
throw new IllegalArgumentException JavaDoc("Object " + object + " is not in pool " + poolName + "!");
742             if (!rec.isInUse()) return; // Must have been released by GC?
743
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             } // end of if ()
752

753         }//end of synch on objects
754
if (removed) {
755             try {
756                 factory.deleteObject(pooled);
757             } catch (Exception JavaDoc e) {
758                 log.error("Pool " + this + " factory (" + factory.getClass().getName() + " delete error: ", e);
759             }
760             fillToMin();
761             /*FIXME --MINSIZE
762               if(objects.size() < minSize)
763               createNewObject(null);
764             */

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         /*
774         if(blocking)
775         {
776            synchronized(this)
777            {
778               notify();
779            }
780         }
781         */

782     }
783
784     private int getUsedCount() {
785         int total = 0;
786         synchronized (objects) {
787             for (Iterator JavaDoc it = new HashSet JavaDoc(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     /**
797      * Returns the pool name and status.
798      */

799     public String JavaDoc toString() {
800         return poolName + " [" + getUsedCount() + "/" + (objects == null ? 0 : objects.size()) + "/" + (maxSize == 0 ? "Unlimited" : Integer.toString(maxSize)) + "]";
801     }
802
803
804     // ---- PoolEventListener Implementation ----
805

806     /**
807      * If the object has been closed, release it.
808      */

809     public void objectClosed(PoolEvent evt) {
810         releaseObject(evt.getSource());
811     }
812
813     /**
814      * If the invalidateOnError flag is set, the object will be removed from
815      * the pool entirely when the client has finished with it.
816      */

817     public void objectError(PoolEvent evt) {
818         if (invalidateOnError || evt.isCatastrophic())
819             markObjectAsInvalid(evt.getSource());
820     }
821
822     /**
823      * If we're tracking the last used times, update the last used time for the
824      * specified object.
825      */

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     // Allow GC if we're within 10% of the desired interval
843
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 JavaDoc objsCopy;
859             synchronized (objects) {
860                 objsCopy = new HashSet JavaDoc(objects.values());
861             }
862
863             if (runGC) { // Garbage collection - return any object that's been out too long with no use
864
Iterator JavaDoc 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) { // Shrinking the pool - remove objects from the pool if they have not been used in a long time
873
// Find object eligible for removal
874
HashSet JavaDoc eligible = new HashSet JavaDoc();
875                 Iterator JavaDoc 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                 // Calculate max number of objects to remove without replacing
882
int max = Math.round(eligible.size() * maxIdleShrinkPercent);
883                 if (max == 0 && eligible.size() > 0) max = 1;
884                 int count = 0;
885                 // Attempt to remove that many objects
886
it = eligible.iterator();
887                 while (it.hasNext()) {
888                     try {
889                         // Delete the object
890
ObjectRecord rec = (ObjectRecord) it.next();
891                         if (rec != null) {
892                             rec.setInUse(true); // Don't let someone use it while we destroy it
893
Object JavaDoc pooled = rec.getObject();
894                             synchronized (objects) {
895                                 objects.remove(pooled);
896                             }
897                             //removeObject(pooled);
898
try {
899                                 factory.deleteObject(pooled);
900                             } catch (Exception JavaDoc e) {
901                                 log.error("Pool " + this + " factory (" + factory.getClass().getName() + " delete error: ", e);
902                             }
903                             rec.close();
904                             ++count;
905                         }
906                         fillToMin();
907                         /*FIXME --MINSIZE
908                                     if(count > max || objects.size() < minSize)
909                                        createNewObject(null);
910
911                         */

912                     } catch (ConcurrentModificationException JavaDoc e) {
913                     }
914                 }
915             }
916         }
917         lastGC = System.currentTimeMillis();
918     }
919
920     /**
921      * Removes an object from the pool. Only one thread can add or remove
922      * an object at a time.
923      */

924     /* private void removeObject(Object pooled)
925     {
926        synchronized(objects)
927        {
928           objects.remove(pooled);
929        }
930     }
931     */

932     /**
933      * Creates a new Object.
934      * @param parameters If <b>true</b>, then the object is locked and
935      * translated by the factory, and the resulting object
936      * returned. If <b>false</b>, then the object is left in the
937      * pool unlocked.
938      */

939     private ObjectRecord createNewObject(Object JavaDoc parameters) {
940         Object JavaDoc ob = null;
941         try {
942             ob = factory.createObject(parameters);
943         } catch (Exception JavaDoc ex) {
944             throw new RuntimeException JavaDoc("Could not create connection");
945         }
946         if (ob != null) { // if factory can create object
947
ObjectRecord rec = new ObjectRecord(ob);
948             synchronized (objects) {
949                 objects.put(ob, rec);
950             }
951             return rec;
952         } else {
953             throw new RuntimeException JavaDoc("could not create new object!");
954         }
955     }
956
957     public void fillToMin() {
958         Collection JavaDoc newMCs = new ArrayList JavaDoc();
959         try {
960             while (objects.size() < minSize) {
961                 newMCs.add(getObject(null));
962             } // end of while ()
963
} catch (Exception JavaDoc re) {
964             //Whatever the reason, stop trying to add more!
965
} // end of try-catch
966
for (Iterator JavaDoc i = newMCs.iterator(); i.hasNext();) {
967             releaseObject(i.next());
968         } // end of for ()
969

970     }
971
972 }
973
974 class BeanFactory extends PoolObjectFactory {
975
976     private Class JavaDoc beanClass;
977
978     private Logger log = Logger.getLogger(BeanFactory.class);
979
980     public BeanFactory(Class JavaDoc beanClass) {
981         try {
982             beanClass.getConstructor(new Class JavaDoc[0]);
983         } catch (NoSuchMethodException JavaDoc e) {
984             throw new IllegalArgumentException JavaDoc("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 JavaDoc createObject(Object JavaDoc parameters) {
994         try {
995             return beanClass.newInstance();
996         } catch (Exception JavaDoc e) {
997             log.error("Unable to create instance of " + beanClass.getName(), e);
998         }
999         return null;
1000    }
1001}
1002
Popular Tags