KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > snaq > util > ObjectPool


1 /*
2     DBPool - JDBC Connection Pool Manager
3     Copyright (c) Giles Winstanley
4 */

5 package snaq.util;
6
7 import java.io.*;
8 import java.text.*;
9 import java.util.*;
10
11 /**
12  * Base class for a pool system implementation.
13  * This class provides all the base functionality required and can be easily
14  * extended to provide pooling support for many different types of object.
15  * <p>New objects are retrieved on demand according to specified limits,
16  * and the pool can also ensure an object's validity before returning it.
17  * The limits which can be set for a pool include the number of items to be
18  * held in the pool, and the maximum number to ever be created.
19  * <p>The pool will try to maintain <tt>poolSize</tt> items open and ready
20  * for use (unless that number has not yet been created), but if expiry is
21  * enabled this number will reduce if the items are not used frequently.
22  * If <tt>maxSize</tt> is specified then the pool will never create more
23  * than this number of items, and another can only be obtained from the pool
24  * if it is handed back by another client.
25  * <p><tt>ObjectPool</tt> should be sub-classed to override
26  * the following methods:</p>
27  * <pre>
28  * protected Reusable create() throws Exception
29  * protected boolean isValid(final Reusable o)
30  * protected void destroy(final Reusable o)
31  * </pre>
32  * <p>It is recommended that the sub-class implements methods for obtaining
33  * and returning items within the pool, casting the objects returned by this
34  * class as required to the appropriate class type.
35  * <p>ObjectPool also support asynchronous destruction of items, which can be
36  * useful in circumstances where destruction of items held can take a long
37  * time which would delay the <tt>checkIn</tt> method. This also applies
38  * to the release of the pool after it's final use, which should always be
39  * done using either <tt>release</tt> or <tt>releaseAsync</tt>.
40  * @author Giles Winstanley
41  */

42 public abstract class ObjectPool extends LogUtil
43 {
44     // Pool access method
45
private static final int ACCESS_FIFO = 1;
46     private static final int ACCESS_LIFO = 2;
47     private static final int ACCESS_RANDOM = 3;
48     private int accessMethod = ACCESS_LIFO;
49     // Other variables
50
private String JavaDoc name;
51     private List free, used;
52     private int poolSize, maxSize;
53     private long expiryTime;
54     private long requests, hits;
55     private boolean released = false;
56     private boolean asyncDestroy = false;
57     private DateFormat dateFormat;
58     private Cleaner cleaner;
59     private InitThread initer;
60     private static int cleanerCount = 0;
61     private List listeners = new ArrayList();
62
63
64     /**
65      * Creates new object pool.
66      * @param name pool name
67      * @param poolSize maximum number of pooled objects, or 0 for no limit
68      * @param maxSize maximum number of possible objects, or 0 for no limit
69      * @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
70      */

71     protected ObjectPool(String JavaDoc name, int poolSize, int maxSize, long expiryTime)
72     {
73         Class JavaDoc type = getPoolClass();
74         if (!List.class.isAssignableFrom(type))
75             throw new RuntimeException JavaDoc("Invalid pool class type specified: " + type.getName() + " (must implement java.util.List)");
76         try
77         {
78             free = (List)type.newInstance();
79             used = (List)type.newInstance();
80         }
81         catch (Exception JavaDoc e)
82         {
83             throw new RuntimeException JavaDoc("Unable to instantiate pool class type: " + type.getName());
84         }
85         this.name = name;
86         this.setParameters(poolSize, maxSize, expiryTime);
87     }
88
89     /**
90      * Creates new object pool.
91      * @param name pool name
92      * @param poolSize maximum number of pooled objects, or 0 for no limit
93      * @param maxSize maximum number of possible objects, or 0 for no limit
94      * @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
95      */

96     protected ObjectPool(String JavaDoc name, int poolSize, int maxSize, int expiryTime)
97     {
98         this(name, poolSize, maxSize, (long)expiryTime);
99     }
100
101
102     /**
103      * Initializes the given number of items in the pool.
104      * This spawns a new thread to create them in the background.
105      */

106     public final void init(int num)
107     {
108         if (num == 0)
109             return;
110         if (num > 0 && num <= getMaxSize())
111         {
112             if (initer != null)
113             {
114                 initer.halt();
115                 try { initer.join(); } catch (InterruptedException JavaDoc ie) {}
116             }
117             initer = new InitThread(num);
118             initer.start();
119         }
120         else
121             throw new IllegalArgumentException JavaDoc("Invalid number of items specified for initialization");
122     }
123
124     /**
125      * Checks out an item from the pool. If no free item
126      * is available, a new item is created unless the maximum
127      * number of items has been reached. If a free item
128      * is not valid it is removed from the pool and another
129      * is retrieved.
130      * @return item from the pool, or null if nothing available
131      * @exception Exception if there is an error creating a new object
132      */

133     protected final synchronized Reusable checkOut() throws Exception JavaDoc
134     {
135         if (released)
136             throw new RuntimeException JavaDoc("Pool no longer valid for use");
137         int oldTotalConns = used.size() + free.size();
138
139         TimeWrapper tw = null;
140         Reusable o = null;
141         if (free.size() > 0)
142         {
143             // Get an object from the free list
144
switch(accessMethod)
145             {
146                 case ACCESS_FIFO:
147                     tw = (TimeWrapper)free.remove(0);
148                     break;
149                 case ACCESS_RANDOM:
150                     tw = (TimeWrapper)free.remove((int)(free.size() * Math.random()));
151                     break;
152                 case ACCESS_LIFO:
153                 default:
154                     tw = (TimeWrapper)free.remove(free.size() - 1);
155             }
156             o = (Reusable)tw.getObject();
157             boolean valid = isValid(o);
158             while (!valid && free.size() > 0)
159             {
160                 destroyObject(o);
161                 log("Removed invalid item from pool");
162                 tw = (TimeWrapper)free.remove(0);
163                 o = (Reusable)tw.getObject();
164                 valid = isValid(o);
165             }
166             if (free.size() == 0 && !valid)
167                 o = null;
168         }
169         boolean hit = (o != null);
170
171         // If no free items and can create more...create new item
172
if (o == null)
173         {
174             if (maxSize > 0 && used.size() == maxSize)
175                 fireMaxSizeLimitErrorEvent();
176             else if (used.size() < maxSize || maxSize == 0)
177             {
178                 o = create();
179                 if (!isValid(o))
180                     throw new RuntimeException JavaDoc("Unable to create a valid connection");
181             }
182         }
183
184         // If a connection has been obtained/created, add it to used items collection
185
if (o != null)
186         {
187             used.add(o);
188             requests++;
189             if (hit)
190                 hits++;
191             firePoolCheckOutEvent();
192             // Check for limit reaching so events can be fired.
193
// (Events only fired on increase of connection numbers).
194
int totalConns = used.size() + free.size();
195             if (totalConns == poolSize && totalConns > oldTotalConns)
196                 fireMaxPoolLimitReachedEvent();
197             else if (totalConns == poolSize + 1 && totalConns > oldTotalConns)
198                 fireMaxPoolLimitExceededEvent();
199             if (totalConns == maxSize && totalConns > oldTotalConns)
200                 fireMaxSizeLimitReachedEvent();
201         }
202         if (debug)
203         {
204             String JavaDoc ratio = used.size() + "/" + (used.size() + free.size());
205             String JavaDoc hitRate = " (HitRate=" + getHitRate() + "%)";
206             log("Checkout - " + ratio + hitRate + (o == null ? " - null returned" : ""));
207         }
208         return o;
209     }
210
211
212     /**
213      * Checks out an item from the pool.
214      * If there is no pooled item available and the maximum number
215      * possible has not been reached, another is created.
216      * If a free item is detected as being invalid it is removed
217      * from the pool and the another is retrieved.
218      * If an item is not available and the maximum number possible
219      * has been reached, the method waits for the timeout period
220      * for one to become available by being checked in.
221      * @param timeout timeout value in milliseconds
222      * @return item from the pool, or null if nothing available within timeout period
223      * @exception Exception if there is an error creating a new object
224      */

225     protected final synchronized Reusable checkOut(long timeout) throws Exception JavaDoc
226     {
227         long time = System.currentTimeMillis();
228         Reusable o = null;
229         o = checkOut();
230         while (o == null && (System.currentTimeMillis() - time < timeout))
231         {
232             try
233             {
234                 if (debug)
235                     log("No pooled items spare...waiting for up to " + timeout + "ms");
236                 wait(timeout);
237                 o = checkOut();
238             }
239             catch (InterruptedException JavaDoc e) { log(e, "Connection checkout interrupted"); }
240         }
241         return o;
242     }
243
244
245     /**
246      * Checks an object into the pool, and notify other
247      * threads that may be waiting for one.
248      * @param o object to check in
249      */

250     protected final void checkIn(Reusable o)
251     {
252         if (o == null)
253         {
254             log("Attempt to return null item");
255             return;
256         }
257
258         synchronized(this)
259         {
260             firePoolCheckInEvent();
261
262             // Check if item is from this pool
263
if (!used.remove(o))
264             {
265                 log("Attempt to return item not belonging to pool");
266                 throw new RuntimeException JavaDoc("Attempt to return item not belonging to pool " + name);
267             }
268
269             // If pool is full destroy object, else place in pool
270
boolean kill = maxSize > 0 && (free.size() + used.size() >= poolSize);
271             kill = kill || (maxSize == 0 && free.size() >= poolSize);
272             if (kill)
273             {
274                 destroyObject(o);
275                 if (debug)
276                     log("Checkin* - " + used.size() + "/" + (used.size()+free.size()));
277             }
278             else
279             {
280                 try
281                 {
282                     // Recycle object for next use
283
o.recycle();
284                     // Add object to free list
285
free.add(new TimeWrapper(null, o, expiryTime));
286                     if (debug)
287                         log("Checkin - " + used.size() + "/" + (used.size()+free.size()));
288                     notifyAll();
289                 }
290                 catch (Exception JavaDoc e)
291                 {
292                     // If unable to recycle object, destroy it
293
destroyObject(o);
294                     log(e, "Unable to recycle item - destroyed");
295                 }
296             }
297         }
298     }
299
300
301     /**
302      * Releases all items from the pool, and shuts the pool down.
303      * If any items are still checked-out, this method waits until all items have
304      * been checked-in before returning.
305      */

306     public final void release() { release(false); }
307
308     /**
309      * Releases all items from the pool, and shuts the pool down.
310      * This method returns immediately; a background thread is created to perform the release.
311      */

312     public final synchronized void releaseAsync() { releaseAsync(false); }
313
314     /**
315      * Forcibly releases all items from the pool, and shuts the pool down.
316      * If any items are still checked-out, this method forcibly destroys them
317      * and then returns.
318      */

319     public final void releaseForcibly() { release(true); }
320
321     /**
322      * Releases all items from the pool, and shuts the pool down.
323      * @param forced whether to forcibly destroy items, or let them be checked-in
324      */

325     private final void release(boolean forced)
326     {
327         // Set released flag to prevent check-out of new items
328
if (released)
329             return;
330         released = true;
331         // Destroy cleaner thread
332
if (cleaner != null)
333         {
334             cleaner.halt();
335             try { cleaner.join(); }
336             catch (InterruptedException JavaDoc ie) { log(ie, "Interrupted during halting of old cleaner thread"); }
337             cleaner = null;
338         }
339
340         synchronized(this)
341         {
342             int rel = 0, failed = 0;
343             TimeWrapper tw = null;
344             Reusable o = null;
345             // Destroy all items still in use
346
if (forced)
347             {
348                 for (Iterator iter = used.iterator(); iter.hasNext();)
349                 {
350                     o = (Reusable)iter.next();
351                     try
352                     {
353                         destroy(o);
354                         rel++;
355                     }
356                     catch (Exception JavaDoc ex)
357                     {
358                         failed++;
359                         log(ex, "Unable to release item in pool");
360                     }
361                 }
362                 used.clear();
363             }
364             else
365             {
366                 if (debug && used.size() > 0)
367                     log("Waiting for used items to be checked-in...");
368                 while (used.size() > 0)
369                 {
370                     try { wait(); }
371                     catch (InterruptedException JavaDoc e) {}
372                 }
373             }
374
375             // Destroy all currently free items
376
for (Iterator iter = free.iterator(); iter.hasNext();)
377             {
378                 tw = (TimeWrapper)iter.next();
379                 o = (Reusable)tw.getObject();
380                 try
381                 {
382                     destroy(o);
383                     rel++;
384                 }
385                 catch (Exception JavaDoc ex)
386                 {
387                     failed++;
388                     log(ex, "Unable to release item in pool");
389                 }
390             }
391             free.clear();
392
393             // Destroy log reference
394
if (debug)
395             {
396                 String JavaDoc s = "Released " + rel + (rel > 1 ? " items" : " item");
397                 if (failed > 0)
398                     s += " (failed to release " + failed + (failed > 1 ? " items)" : " item)");
399                 log(s);
400             }
401             firePoolReleasedEvent();
402             listeners.clear();
403             super.close();
404         }
405     }
406
407     /**
408      * Releases all items from the pool, and shuts the pool down.
409      * This method returns immediately; a background thread is created to perform the release.
410      */

411     private final void releaseAsync(final boolean forced)
412     {
413         Thread JavaDoc t = new Thread JavaDoc(new Runnable JavaDoc()
414         {
415             public void run() { release(forced); }
416         });
417         t.start();
418     }
419
420
421     /**
422      * Object creation method.
423      * This method is called when a new item needs to be created following a call
424      * to one of the check-out methods.
425      * @exception Exception if unable to create the item
426      */

427     protected abstract Reusable create() throws Exception JavaDoc;
428
429     /**
430      * Object validation method.
431      * This method is called when checking-out an item to see if it is valid for use.
432      * When overridden by the sub-class it is recommended that this method perform
433      * suitable checks to ensure the object can be used without problems.
434      */

435     protected abstract boolean isValid(final Reusable o);
436
437     /**
438      * Object destruction method.
439      * This method is called when an object needs to be destroyed due to pool
440      * pruning/cleaning, or final release of the pool.
441      */

442     protected abstract void destroy(final Reusable o);
443
444     /**
445      * Destroys the given object (asynchronously if necessary).
446      */

447     private final void destroyObject(final Reusable o)
448     {
449         if (o == null)
450             return;
451         if (asyncDestroy)
452         {
453             Thread JavaDoc t = new Thread JavaDoc(new Runnable JavaDoc()
454             {
455                 public void run() { destroy(o); }
456             });
457             t.start();
458         }
459         else
460             destroy(o);
461     }
462
463     /**
464      * Determines whether to perform asynchronous object destruction.
465      * If set to true then each time an object is destroyed (invalid object
466      * during pool operation, or when the pool is finally released) the operation
467      * is done in a separate thread, allowing the method to return immediately.
468      * This can be useful when calling the destroy method on an object takes a
469      * long time to complete.
470      */

471     public final void setAsyncDestroy(boolean b) { asyncDestroy = b; }
472
473     /**
474      * Returns whether asynchronous object destruction is enabled.
475      * (Default: false)
476      */

477     public final boolean isAsyncDestroy() { return asyncDestroy; }
478
479     /**
480      * Writes a message to the log.
481      */

482     public void log(String JavaDoc logEntry)
483     {
484         log(name + ": ", logEntry);
485     }
486
487     /**
488      * Writes a message with an Exception to the log file.
489      */

490     public void log(Throwable JavaDoc e, String JavaDoc logEntry)
491     {
492         log(e, name + ": ", logEntry);
493     }
494
495     /**
496      * Returns the pool name.
497      */

498     public final String JavaDoc getName() { return this.name; }
499
500     /**
501      * Returns the maximum size of the pool.
502      */

503     public final int getPoolSize() { return poolSize; }
504
505     /**
506      * Returns the maximum number of items that can be created.
507      */

508     public final int getMaxSize() { return maxSize; }
509
510     /**
511      * Returns the expiry time for unused items in the pool.
512      */

513     public final long getExpiryTime() { return expiryTime; }
514
515     /**
516      * Sets the pooling parameters.
517      * Any items currently in the pool will remain, subject to the new parameters.
518      * (The hit rate counters are reset when this method is called.)
519      * @param poolSize number of items to be keep in pool
520      * @param maxSize maximum number of items to be created
521      * @param expiryTime expiry time for unused items (milliseconds) (0 = no expiry)
522      */

523     public final void setParameters(int poolSize, int maxSize, long expiryTime)
524     {
525         synchronized(this)
526         {
527             if (cleaner != null)
528                 cleaner.halt();
529
530             this.poolSize = Math.max(poolSize, 0);
531             this.maxSize = Math.max(maxSize, 0);
532             this.expiryTime = Math.max(expiryTime, 0);
533             if (this.maxSize > 0 && this.maxSize < this.poolSize)
534                 this.maxSize = this.poolSize;
535             resetHitCounter();
536
537             // Update pooled items to use new expiry
538
TimeWrapper tw = null;
539             for (Iterator iter = free.iterator(); iter.hasNext();)
540             {
541                 tw = (TimeWrapper)iter.next();
542                 tw.setLiveTime(expiryTime);
543             }
544             // Creates cleaner thread with check interval of at most 5 seconds.
545
if (this.expiryTime > 0)
546             {
547                 long iVal = Math.min(5000, this.expiryTime / 5);
548                 (cleaner = new Cleaner(this, iVal)).start();
549             }
550         }
551         if (debug)
552         {
553             String JavaDoc info = "pool=" + this.poolSize + ",max=" + this.maxSize + ",expiry=";
554             info += this.expiryTime == 0 ? "none" : this.expiryTime + "ms";
555             log("Parameters changed (" + info + ")");
556         }
557         fireParametersChangedEvent();
558     }
559
560     /**
561      * Returns the total number of objects held (available and checked-out).
562      */

563     public final synchronized int getSize() { return free.size() + used.size(); }
564
565     /**
566      * Returns the number of items that are currently checked-out.
567      */

568     public final synchronized int getCheckedOut() { return used.size(); }
569
570     /**
571      * Returns the number of items held in the pool that are free to be checked-out.
572      */

573     public final synchronized int getFreeCount() { return free.size(); }
574
575     /**
576      * Returns hit rate of the pool as a percentage.
577      * The hit rate is the proportion of requests for a connection which result
578      * in the return of a connection which is in the pool, rather than which
579      * results in the creation of a new connection.
580      */

581     public final float getHitRate() { return (requests == 0) ? 0 : (((float)hits / requests) * 100f); }
582
583     /**
584      * Resets the counter for determining the pool's hit rate.
585      */

586     protected final void resetHitCounter() { requests = hits = 0; }
587
588     /**
589      * Sets the pool access method to FIFO (first-in, first-out: a queue).
590      */

591     protected final void setAccessFIFO() { accessMethod = ACCESS_FIFO; }
592
593     /**
594      * Sets the pool access method to LIFO (last-in, first-out: a stack).
595      */

596     protected final void setAccessLIFO() { accessMethod = ACCESS_LIFO; }
597
598     /**
599      * Sets the pool access method to random (a random connection is selected for check-out).
600      */

601     protected final void setAccessRandom() { accessMethod = ACCESS_RANDOM; }
602
603     /**
604      * Returns the class to use for the pool collection.
605      * This can be over-ridden by a sub-class to provide a different List
606      * type for the pool, which may give performance benefits in certain situations.
607      * Only instances of List collections should be used.
608      * (Default: java.util.ArrayList.class)
609      * For those wanting to override this method, pool items are checked-out from
610      * the front of the List - <tt>remove(0)</tt> - and replaced at the end of
611      * the List when checked-in again - <tt>add(Object)</tt>.
612      */

613     protected Class JavaDoc getPoolClass() { return ArrayList.class; }
614
615     /**
616      * Shuts down the object pool.
617      * If overridden the sub-class should make sure super.finalize() is called.
618      */

619     public void finalize()
620     {
621         if (cleaner != null)
622         {
623             cleaner.halt();
624             cleaner = null;
625         }
626         if (initer != null)
627         {
628             initer.halt();
629             initer = null;
630         }
631     }
632
633     /**
634      * Flushes the pool of all currently available items, emptying the pool.
635      */

636     public final void flush()
637     {
638         int count = 0;
639         synchronized(this)
640         {
641             TimeWrapper tw = null;
642             for (Iterator iter = free.iterator(); iter.hasNext();)
643             {
644                 tw = (TimeWrapper)iter.next();
645                 iter.remove();
646                 destroyObject((Reusable)tw.getObject());
647                 count++;
648             }
649         }
650         if (count > 0 && debug)
651             log("Flushed all spare items from pool");
652     }
653
654     /**
655      * Purges expired objects from the pool.
656      * This method is called by the cleaner thread to purge expired items.
657      * @return false if pool is empty after purging (no further purge required until items added), true otherwise
658      */

659     final synchronized boolean purge()
660     {
661         if (debug)
662             log("Checking for expired items");
663         int count = 0;
664         TimeWrapper tw = null;
665         for (Iterator iter = free.iterator(); iter.hasNext();)
666         {
667             tw = (TimeWrapper)iter.next();
668             if (tw.isExpired())
669             {
670                 iter.remove();
671                 destroyObject((Reusable)tw.getObject());
672                 count++;
673             }
674         }
675         return free.size() > 0 || count > 0;
676     }
677
678     //**************************
679
// Event-handling methods
680
//**************************
681

682     /**
683      * Adds an ObjectPoolListener to the event notification list.
684      */

685     public final void addObjectPoolListener(ObjectPoolListener x)
686     {
687         listeners.add(x);
688     }
689
690     /**
691      * Removes an ObjectPoolListener from the event notification list.
692      */

693     public final void removeObjectPoolListener(ObjectPoolListener x)
694     {
695         listeners.remove(x);
696     }
697
698     private final void firePoolCheckOutEvent()
699     {
700         if (listeners.isEmpty())
701             return;
702         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.CHECKOUT);
703         for (Iterator iter = listeners.iterator(); iter.hasNext();)
704             ((ObjectPoolListener)iter.next()).poolCheckOut(poolEvent);
705     }
706
707     private final void firePoolCheckInEvent()
708     {
709         if (listeners.isEmpty())
710             return;
711         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.CHECKIN);
712         for (Iterator iter = listeners.iterator(); iter.hasNext();)
713             ((ObjectPoolListener)iter.next()).poolCheckIn(poolEvent);
714     }
715
716     private final void fireMaxPoolLimitReachedEvent()
717     {
718         if (listeners.isEmpty())
719             return;
720         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_POOL_LIMIT_REACHED);
721         for (Iterator iter = listeners.iterator(); iter.hasNext();)
722             ((ObjectPoolListener)iter.next()).maxPoolLimitReached(poolEvent);
723     }
724
725     private final void fireMaxPoolLimitExceededEvent()
726     {
727         if (listeners.isEmpty())
728             return;
729         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_POOL_LIMIT_EXCEEDED);
730         for (Iterator iter = listeners.iterator(); iter.hasNext();)
731             ((ObjectPoolListener)iter.next()).maxPoolLimitExceeded(poolEvent);
732     }
733
734     private final void fireMaxSizeLimitReachedEvent()
735     {
736         if (listeners.isEmpty())
737             return;
738         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_SIZE_LIMIT_REACHED);
739         for (Iterator iter = listeners.iterator(); iter.hasNext();)
740             ((ObjectPoolListener)iter.next()).maxSizeLimitReached(poolEvent);
741     }
742
743     private final void fireMaxSizeLimitErrorEvent()
744     {
745         if (listeners.isEmpty())
746             return;
747         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.MAX_SIZE_LIMIT_ERROR);
748         for (Iterator iter = listeners.iterator(); iter.hasNext();)
749             ((ObjectPoolListener)iter.next()).maxSizeLimitError(poolEvent);
750     }
751
752     private final void fireParametersChangedEvent()
753     {
754         if (listeners == null || listeners.isEmpty())
755             return;
756         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.PARAMETERS_CHANGED);
757         for (Iterator iter = listeners.iterator(); iter.hasNext();)
758             ((ObjectPoolListener)iter.next()).poolParametersChanged(poolEvent);
759     }
760
761     private final void firePoolReleasedEvent()
762     {
763         if (listeners.isEmpty())
764             return;
765         ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, ObjectPoolEvent.POOL_RELEASED);
766         for (Iterator iter = listeners.iterator(); iter.hasNext();)
767             ((ObjectPoolListener)iter.next()).poolReleased(poolEvent);
768     }
769
770
771     /**
772      * Thread to perform clean-up of expired objects in pool.
773      * Each time nothing is cleaned because the pool is empty the cleaner waits
774      * until an item is returned, when it is woken up and starts cleaning again.
775      */

776     final class Cleaner extends Thread JavaDoc
777     {
778         private ObjectPool pool;
779         private long interval;
780         private boolean stopped;
781
782         Cleaner(ObjectPool pool, long interval)
783         {
784             this.setName("CleanerThread_" + Integer.toString(cleanerCount++));
785             this.pool = pool;
786             this.interval = interval;
787         }
788
789         public void start()
790         {
791             stopped = false;
792             super.start();
793         }
794
795         /**
796          * Safely stops the thread from running.
797          */

798         public void halt()
799         {
800             if (!isHalted())
801             {
802                 stopped = true;
803                 interrupt(); // Wake cleaner if necessary
804
}
805         }
806
807         /**
808          * Returns whether the thread has been halted.
809          */

810         public boolean isHalted() { return stopped; }
811
812         /**
813          * Handles the expiry of old objects.
814          */

815         public void run()
816         {
817             while (pool.cleaner == Thread.currentThread() && !stopped)
818             {
819                 synchronized(pool)
820                 {
821                     if (!pool.purge())
822                     {
823                         try { pool.wait(); }
824                         catch (InterruptedException JavaDoc e) {}
825                     }
826                 }
827                 if (!stopped)
828                 {
829                     try { sleep(interval); }
830                     catch (InterruptedException JavaDoc e) {}
831                 }
832             }
833         }
834     }
835
836
837     /**
838      * Thread to initialize items in pool.
839      * This thread simply performs a check-out/in of new items up to the specified
840      * number to ensure the pool is populated.
841      */

842     private class InitThread extends Thread JavaDoc
843     {
844         private int num;
845         private boolean stopped = false;
846
847         private InitThread(int num)
848         {
849             // Ensure 0 < num < poolSize.
850
this.num = Math.min(poolSize, Math.max(num, 0));
851         }
852
853         public void halt() { stopped = true; }
854
855         /**
856          * Populates the pool with the given number of database connections.
857          * If the pool already contains open connections then they will be counted
858          * towards the number created by this method.
859          */

860         public void run()
861         {
862             if (num > 0 && num <= poolSize && getSize() < num)
863             {
864                 int count = 0;
865                 while (!stopped && getSize() < num && num <= poolSize)
866                 {
867                     try
868                     {
869                         Reusable o = create();
870                         if (o == null)
871                             throw new RuntimeException JavaDoc("Null item created");
872                         else
873                         {
874                             free.add(new TimeWrapper(null, o, expiryTime));
875                             count++;
876                             if (debug)
877                                 log("Initialized new item in pool");
878                         }
879                     }
880                     catch (Exception JavaDoc ex)
881                     {
882                         log(ex, "Unable to initialize items in pool");
883                         stopped = true;
884                     }
885                 }
886                 if (debug)
887                     log("Initialized pool with " + count + " new item" + (count > 1 ? "s" : ""));
888             }
889         }
890     }
891 }
Popular Tags