KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > excalibur > pool > ResourceLimitingPool


1 /*
2
3  ============================================================================
4                    The Apache Software License, Version 1.1
5  ============================================================================
6  
7  Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  
9  Redistribution and use in source and binary forms, with or without modifica-
10  tion, are permitted provided that the following conditions are met:
11  
12  1. Redistributions of source code must retain the above copyright notice,
13     this list of conditions and the following disclaimer.
14  
15  2. Redistributions in binary form must reproduce the above copyright notice,
16     this list of conditions and the following disclaimer in the documentation
17     and/or other materials provided with the distribution.
18  
19  3. The end-user documentation included with the redistribution, if any, must
20     include the following acknowledgment: "This product includes software
21     developed by the Apache Software Foundation (http://www.apache.org/)."
22     Alternately, this acknowledgment may appear in the software itself, if
23     and wherever such third-party acknowledgments normally appear.
24  
25  4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
26     must not be used to endorse or promote products derived from this software
27     without prior written permission. For written permission, please contact
28     apache@apache.org.
29  
30  5. Products derived from this software may not be called "Apache", nor may
31     "Apache" appear in their name, without prior written permission of the
32     Apache Software Foundation.
33  
34  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45  This software consists of voluntary contributions made by many individuals
46  on behalf of the Apache Software Foundation. For more information on the
47  Apache Software Foundation, please see <http://www.apache.org/>.
48  
49 */

50 package org.apache.avalon.excalibur.pool;
51
52 import java.util.Iterator JavaDoc;
53 import java.util.LinkedList JavaDoc;
54
55 import org.apache.avalon.framework.activity.Disposable;
56 import org.apache.avalon.framework.logger.AbstractLogEnabled;
57 import org.apache.avalon.framework.logger.LogEnabled;
58 import org.apache.avalon.framework.thread.ThreadSafe;
59 import org.apache.excalibur.instrument.CounterInstrument;
60 import org.apache.excalibur.instrument.Instrument;
61 import org.apache.excalibur.instrument.Instrumentable;
62 import org.apache.excalibur.instrument.ValueInstrument;
63
64 /**
65  * General Pool implementation which supports; weak and strong pool size limits,
66  * optional blocking gets when poolables are not available, and automatic pool
67  * trimming of unused poolables.
68  * <p>
69  * Whenever get() is called, the pool tests to see whether it is time to trim old
70  * poolables from the pool. If any old poolables exist then they are removed at
71  * this time. This means that old poolables will not be removed if get() is never
72  * called. Applications can optionally call trim() to force old objects to be
73  * trimmed. See the {@link #trim()} method for details of how trimming works.
74  *
75  * @author <a HREF="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
76  * @version CVS $Revision: 1.7 $ $Date: 2003/02/25 16:28:49 $
77  * @since 4.1
78  */

79 public class ResourceLimitingPool
80     extends AbstractLogEnabled
81     implements Pool, LogEnabled, Disposable, ThreadSafe, Instrumentable
82 {
83     public static final String JavaDoc DEFAULT_INSTRUMENTABLE_NAME = "pool";
84     public static final String JavaDoc INSTRUMENT_SIZE_NAME = "size";
85     public static final String JavaDoc INSTRUMENT_READY_SIZE_NAME = "ready-size";
86     public static final String JavaDoc INSTRUMENT_GETS_NAME = "gets";
87     public static final String JavaDoc INSTRUMENT_PUTS_NAME = "puts";
88     public static final String JavaDoc INSTRUMENT_BLOCKS_NAME = "blocks";
89     public static final String JavaDoc INSTRUMENT_CREATES_NAME = "creates";
90     public static final String JavaDoc INSTRUMENT_DECOMMISSIONS_NAME = "decommissions";
91
92     /*---------------------------------------------------------------
93      * Protected Fields
94      *-------------------------------------------------------------*/

95     /**
96      * Object used to synchronize access to the get and put methods
97      */

98     protected final Object JavaDoc m_semaphore = new Object JavaDoc();
99
100     /*---------------------------------------------------------------
101      * Private Fields
102      *-------------------------------------------------------------*/

103     /**
104      * Keeps track of whether or not the Pool has been disposed.
105      */

106     private boolean m_disposed = false;
107
108     /**
109      * The Object Factory used to generate new Poolable instances for the pool.
110      */

111     private final ObjectFactory m_factory;
112
113     /**
114      * The maximum size of the pool.
115      */

116     private final int m_max;
117
118     /**
119      * Whether or not the pool allows for the creation of objects beyond the maximum pool size.
120      */

121     private final boolean m_maxStrict;
122
123     /**
124      * Whether or not the pool should cause threads requesting a Poolable to block when m_maxStrict
125      * is true, the pool size is equal to m_max and there are no Poolable instances available.
126      */

127     private final boolean m_blocking;
128
129     /**
130      * The maximum amount of time in milliseconds that the pool will block. If 0, blocking will
131      * wait indeffinately.
132      */

133     private final long m_blockTimeout;
134
135     /**
136      * The minimum interval with which old unused poolables will be removed from the pool.
137      */

138     private final long m_trimInterval;
139
140     /**
141      * The last time that the pool was trimmed.
142      */

143     private long m_lastTrim;
144
145     /**
146      * List of the Poolable instances which are available for use.
147      */

148     private LinkedList JavaDoc m_ready;
149
150     /**
151      * Store the size of the ready list to optimize operations which require this value.
152      */

153     private int m_readySize;
154
155     /**
156      * List of the Poolable instance which are available for use but have been idle for a while.
157      */

158     private LinkedList JavaDoc m_oldReady;
159
160     /**
161      * Store the size of the old ready list to optimize operations which require this value.
162      */

163     private int m_oldReadySize;
164
165     /**
166      * Total number of Poolable instances in the pool
167      */

168     private int m_size;
169
170     /** Instrumentable Name assigned to this Instrumentable */
171     private String JavaDoc m_instrumentableName = DEFAULT_INSTRUMENTABLE_NAME;
172
173     /** Instrument used to profile the size of the pool. */
174     private ValueInstrument m_sizeInstrument;
175
176     /** Instrument used to profile the number of available poolables. */
177     private ValueInstrument m_readySizeInstrument;
178
179     /** Instrument used to profile the number of gets. */
180     private CounterInstrument m_getsInstrument;
181
182     /** Instrument used to profile the number of puts. */
183     private CounterInstrument m_putsInstrument;
184
185     /** Instrument used to profile the number of blocks. */
186     private CounterInstrument m_blocksInstrument;
187
188     /** Instrument used to profile the number of created poolables. */
189     private CounterInstrument m_createsInstrument;
190
191     /** Instrument used to profile the number of decommissioned poolables. */
192     private CounterInstrument m_decommissionsInstrument;
193
194     /*---------------------------------------------------------------
195      * Constructors
196      *-------------------------------------------------------------*/

197     /**
198      * Creates a new ResourceLimitingPool
199      *
200      * @param factory The ObjectFactory which will be used to create new Poolables as needed by
201      * the pool.
202      * @param max Maximum number of Poolables which can be stored in the pool, 0 implies no limit.
203      * @param maxStrict true if the pool should never allow more than max Poolable to be created.
204      * Will cause an exception to be thrown if more than max Poolables are requested and blocking
205      * is false.
206      * @param blocking true if the pool should cause a thread calling get() to block when Poolables
207      * are not currently available in the pool.
208      * @param blockTimeout The maximum amount of time, in milliseconds, that a call to get() will
209      * block before an exception is thrown. A value of 0 implies an indefinate wait.
210      * @param trimInterval The minimum interval with which old unused poolables will be removed
211      * from the pool. A value of 0 will cause the pool to never trim poolables.
212      */

213     public ResourceLimitingPool( final ObjectFactory factory,
214                                  int max,
215                                  boolean maxStrict,
216                                  boolean blocking,
217                                  long blockTimeout,
218                                  long trimInterval )
219     {
220         m_factory = factory;
221         m_max = ( max <= 0 ? Integer.MAX_VALUE : max );
222         m_maxStrict = maxStrict;
223         m_blocking = blocking;
224         m_blockTimeout = blockTimeout;
225         m_trimInterval = trimInterval;
226
227         // Create the pool lists.
228
m_ready = new LinkedList JavaDoc();
229         if( m_trimInterval > 0 )
230         {
231             m_oldReady = new LinkedList JavaDoc();
232         }
233
234         // Initialize the Instrumentable elements.
235
m_sizeInstrument = new ValueInstrument( INSTRUMENT_SIZE_NAME );
236         m_readySizeInstrument = new ValueInstrument( INSTRUMENT_READY_SIZE_NAME );
237         m_getsInstrument = new CounterInstrument( INSTRUMENT_GETS_NAME );
238         m_putsInstrument = new CounterInstrument( INSTRUMENT_PUTS_NAME );
239         m_blocksInstrument = new CounterInstrument( INSTRUMENT_BLOCKS_NAME );
240         m_createsInstrument = new CounterInstrument( INSTRUMENT_CREATES_NAME );
241         m_decommissionsInstrument = new CounterInstrument( INSTRUMENT_DECOMMISSIONS_NAME );
242     }
243
244     /*---------------------------------------------------------------
245      * Pool Methods
246      *-------------------------------------------------------------*/

247     /**
248      * Gets a Poolable from the pool. If there is room in the pool, a new Poolable will be
249      * created. Depending on the parameters to the constructor, the method may block or throw
250      * an exception if a Poolable is not available on the pool.
251      *
252      * @return Always returns a Poolable. Contract requires that put must always be called with
253      * the Poolable returned.
254      * @throws Exception An exception may be thrown as described above or if there is an exception
255      * thrown by the ObjectFactory's newInstance() method.
256      */

257     public Poolable get() throws Exception JavaDoc
258     {
259         if( m_disposed ) throw new IllegalStateException JavaDoc( "Already Disposed" );
260
261         Poolable poolable;
262         synchronized( m_semaphore )
263         {
264             // If trimming is enabled then trim if it is time
265
if( ( m_oldReady != null ) &&
266                 ( System.currentTimeMillis() - m_lastTrim >= m_trimInterval ) )
267             {
268                 trimInner();
269             }
270
271             // Look for a Poolable at the end of the m_ready list
272
if( m_readySize > 0 )
273             {
274                 // A poolable is ready and waiting in the pool
275
poolable = (Poolable)m_ready.removeLast();
276                 m_readySize--;
277             }
278             else if( m_oldReadySize > 0 )
279             {
280                 // An old poolable is ready and waiting in the pool
281
poolable = (Poolable)m_oldReady.removeLast();
282                 m_oldReadySize--;
283             }
284             else
285             {
286                 // Are we allowed to create a new poolable here?
287
if( ( m_size >= m_max ) && m_maxStrict )
288                 {
289                     // The pool has as many active Poolables as it is allowed and
290
// we are not allowed to create any more.
291

292                     // Are we allowed to wait for a Poolable to become available?
293
if( m_blocking )
294                     {
295                         long blockStart = System.currentTimeMillis();
296
297                         if( getLogger().isDebugEnabled() )
298                         {
299                             getLogger().debug( "Blocking until a Poolable is available. "
300                                                + "Thread: " + Thread.currentThread().getName() );
301                         }
302
303                         // Notify the InstrumentManager
304
m_blocksInstrument.increment();
305
306                         if( m_blockTimeout > 0 )
307                         {
308                             // Wait for a limited amount of time for a poolable is made
309
// available.
310
// Other threads may grab a connection before this thread gets the
311
// semaphore, so be careful.
312
long blockWait = m_blockTimeout;
313                             do
314                             {
315                                 if( blockWait > 0 )
316                                 {
317                                     try
318                                     {
319                                         m_semaphore.wait( blockWait );
320                                     }
321                                     catch( InterruptedException JavaDoc e )
322                                     {
323                                     }
324
325                                     // The dispose() method might have woken us up.
326
if( m_disposed )
327                                     {
328                                         throw new IllegalStateException JavaDoc( "Already Disposed" );
329                                     }
330
331                                     if( m_readySize == 0 )
332                                     {
333                                         // Not available yet, calculate how much longer to wait.
334
long now = System.currentTimeMillis();
335                                         blockWait = m_blockTimeout - ( now - blockStart );
336                                     }
337                                 }
338                                 else
339                                 {
340                                     // We timed out waiting.
341
long now = System.currentTimeMillis();
342
343                                     if( getLogger().isDebugEnabled() )
344                                     {
345                                         getLogger().debug(
346                                             "Timed out waiting for a Poolable to become "
347                                             + "available. Blocked for " + ( now - blockStart )
348                                             + "ms. Thread: " + Thread.currentThread().getName() );
349                                     }
350                                     throw new Exception JavaDoc
351                                         ( "Could not create enough Components to service your "
352                                           + "request (Timed out)." );
353                                 }
354                             } while( m_readySize == 0 );
355                         }
356                         else
357                         {
358                             // Wait until we get a poolable no matter how long it takes.
359
// Other threads may grab a connection before this thread gets the
360
// semaphore, so be careful.
361
do
362                             {
363                                 try
364                                 {
365                                     m_semaphore.wait();
366                                 }
367                                 catch( InterruptedException JavaDoc e )
368                                 {
369                                 }
370
371                                 // The dispose() method might have woken us up.
372
if( m_disposed )
373                                 {
374                                     throw new IllegalStateException JavaDoc( "Already Disposed" );
375                                 }
376                             } while( m_readySize == 0 );
377                         }
378
379                         // A poolable is ready and waiting in the pool
380
poolable = (Poolable)m_ready.removeLast();
381                         m_readySize--;
382
383                         if( getLogger().isDebugEnabled() )
384                         {
385                             long now = System.currentTimeMillis();
386                             getLogger().debug( "Blocked for " + ( now - blockStart ) + "ms "
387                                                + "waiting for a Poolable to become available. "
388                                                + "Thread: " + Thread.currentThread().getName() );
389                         }
390                     }
391                     else
392                     {
393                         // We must fail.
394
throw new Exception JavaDoc
395                             ( "Could not create enough Components to service your request." );
396                     }
397                 }
398                 else
399                 {
400                     // Create a new poolable
401
m_size++;
402                     poolable = newPoolable();
403
404                     if( getLogger().isDebugEnabled() )
405                     {
406                         getLogger().debug( "Created a new " + poolable.getClass().getName()
407                                            + " from the object factory." );
408                     }
409                 }
410             }
411         }
412
413         if( getLogger().isDebugEnabled() )
414         {
415             getLogger().debug( "Got a " + poolable.getClass().getName() + " from the pool." );
416         }
417
418         // Notify the InstrumentManager
419
m_getsInstrument.increment();
420         if( m_readySizeInstrument.isActive() )
421         {
422             m_readySizeInstrument.setValue( getReadySize() );
423         }
424
425         return poolable;
426     }
427
428     /**
429      * Returns a poolable to the pool and notifies any thread blocking.
430      *
431      * @param poolable Poolable to return to the pool.
432      */

433     public void put( Poolable poolable )
434     {
435         // Handle Recyclable objects
436
if( poolable instanceof Recyclable )
437         {
438             ( (Recyclable)poolable ).recycle();
439         }
440
441         synchronized( m_semaphore )
442         {
443             if( m_size <= m_max )
444             {
445                 if( m_disposed )
446                 {
447                     // The pool has already been disposed.
448
if( getLogger().isDebugEnabled() )
449                     {
450                         getLogger().debug( "Put called for a " + poolable.getClass().getName()
451                                            + " after the pool was disposed." );
452                     }
453
454                     permanentlyRemovePoolable( poolable );
455                 }
456                 else
457                 {
458                     // There is room in the pool to keep this poolable.
459
if( getLogger().isDebugEnabled() )
460                     {
461                         getLogger().debug( "Put a " + poolable.getClass().getName()
462                                            + " back into the pool." );
463                     }
464
465                     m_ready.addLast( poolable );
466                     m_readySize++;
467
468                     // Let any waiting threads know that a poolable has become available.
469
if( m_blocking )
470                     {
471                         m_semaphore.notify();
472                     }
473                 }
474             }
475             else
476             {
477                 // More Poolables were created than can be held in the pool, so remove.
478
if( getLogger().isDebugEnabled() )
479                 {
480                     getLogger().debug( "No room to put a " + poolable.getClass().getName()
481                                        + " back into the pool, so remove it." );
482                 }
483
484                 permanentlyRemovePoolable( poolable );
485             }
486         }
487
488         // Notify the InstrumentManager
489
m_putsInstrument.increment();
490         if( m_readySizeInstrument.isActive() )
491         {
492             m_readySizeInstrument.setValue( getReadySize() );
493         }
494     }
495
496     /*---------------------------------------------------------------
497      * Disposable Methods
498      *-------------------------------------------------------------*/

499     /**
500      * The dispose operation is called at the end of a components lifecycle.
501      * This method will be called after Startable.stop() method (if implemented
502      * by component). Components use this method to release and destroy any
503      * resources that the Component owns.
504      */

505     public void dispose()
506     {
507         m_disposed = true;
508
509         // Any Poolables in the m_ready list need to be disposed of
510
synchronized( m_semaphore )
511         {
512             // Remove objects in the ready list.
513
for( Iterator JavaDoc iter = m_ready.iterator(); iter.hasNext(); )
514             {
515                 Poolable poolable = (Poolable)iter.next();
516                 iter.remove();
517                 m_readySize--;
518                 permanentlyRemovePoolable( poolable );
519             }
520
521             // Remove objects in the old ready list.
522
if( m_oldReady != null )
523             {
524                 for( Iterator JavaDoc iter = m_oldReady.iterator(); iter.hasNext(); )
525                 {
526                     Poolable poolable = (Poolable)iter.next();
527                     iter.remove();
528                     m_oldReadySize--;
529                     permanentlyRemovePoolable( poolable );
530                 }
531             }
532
533             // Notify any threads currently waiting for objects so they can abort
534
if( m_blocking )
535             {
536                 m_semaphore.notifyAll();
537             }
538
539             if( ( m_size > 0 ) && getLogger().isDebugEnabled() )
540             {
541                 getLogger().debug( "There were " + m_size
542                                    + " outstanding objects when the pool was disposed." );
543             }
544
545             // Notify the InstrumentManager
546
if( m_sizeInstrument.isActive() )
547             {
548                 m_sizeInstrument.setValue( getSize() );
549             }
550             if( m_readySizeInstrument.isActive() )
551             {
552                 m_readySizeInstrument.setValue( getReadySize() );
553             }
554         }
555     }
556
557     /*---------------------------------------------------------------
558      * Instrumentable Methods
559      *-------------------------------------------------------------*/

560     /**
561      * Sets the name for the Instrumentable. The Instrumentable Name is used
562      * to uniquely identify the Instrumentable during the configuration of
563      * the InstrumentManager and to gain access to an InstrumentableDescriptor
564      * through the InstrumentManager. The value should be a string which does
565      * not contain spaces or periods.
566      * <p>
567      * This value may be set by a parent Instrumentable, or by the
568      * InstrumentManager using the value of the 'instrumentable' attribute in
569      * the configuration of the component.
570      *
571      * @param name The name used to identify a Instrumentable.
572      */

573     public void setInstrumentableName( String JavaDoc name )
574     {
575         m_instrumentableName = name;
576     }
577
578     /**
579      * Gets the name of the Instrumentable.
580      *
581      * @return The name used to identify a Instrumentable.
582      */

583     public String JavaDoc getInstrumentableName()
584     {
585         return m_instrumentableName;
586     }
587
588     /**
589      * Obtain a reference to all the Instruments that the Instrumentable object
590      * wishes to expose. All sampling is done directly through the
591      * Instruments as opposed to the Instrumentable interface.
592      *
593      * @return An array of the Instruments available for profiling. Should
594      * never be null. If there are no Instruments, then
595      * EMPTY_INSTRUMENT_ARRAY can be returned. This should never be
596      * the case though unless there are child Instrumentables with
597      * Instruments.
598      */

599     public Instrument[] getInstruments()
600     {
601         return new Instrument[]
602         {
603             m_sizeInstrument,
604             m_readySizeInstrument,
605             m_getsInstrument,
606             m_putsInstrument,
607             m_blocksInstrument,
608             m_createsInstrument,
609             m_decommissionsInstrument
610         };
611     }
612
613     /**
614      * Any Object which implements Instrumentable can also make use of other
615      * Instrumentable child objects. This method is used to tell the
616      * InstrumentManager about them.
617      *
618      * @return An array of child Instrumentables. This method should never
619      * return null. If there are no child Instrumentables, then
620      * EMPTY_INSTRUMENTABLE_ARRAY can be returned.
621      */

622     public Instrumentable[] getChildInstrumentables()
623     {
624         return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY;
625     }
626
627     /*---------------------------------------------------------------
628      * Methods
629      *-------------------------------------------------------------*/

630     /**
631      * Permanently removes a poolable from the pool's active list and
632      * destroys it so that it will not ever be reused.
633      * <p>
634      * This method is only called by threads that have m_semaphore locked.
635      */

636     protected void permanentlyRemovePoolable( Poolable poolable )
637     {
638         m_size--;
639         removePoolable( poolable );
640     }
641
642     /**
643      * Returns the total number of Poolables created by the pool. Includes active and ready.
644      */

645     public int getSize()
646     {
647         return m_size;
648     }
649
650     /**
651      * Returns the number of available Poolables waiting in the pool.
652      */

653     public int getReadySize()
654     {
655         synchronized( m_semaphore )
656         {
657             return m_readySize + m_oldReadySize;
658         }
659     }
660
661     /**
662      * Create a new poolable instance by by calling the newInstance method
663      * on the pool's ObjectFactory.
664      * <p>
665      * This is the method to override when you need to enforce creational
666      * policies.
667      * <p>
668      * This method is only called by threads that have m_semaphore locked.
669      */

670     protected Poolable newPoolable() throws Exception JavaDoc
671     {
672         Object JavaDoc obj = m_factory.newInstance();
673
674         // Notify the InstrumentManager
675
m_createsInstrument.increment();
676         if( m_sizeInstrument.isActive() )
677         {
678             m_sizeInstrument.setValue( getSize() );
679         }
680
681         return (Poolable)obj;
682     }
683
684     /**
685      * Called when an object is being removed permanently from the pool.
686      * This is the method to override when you need to enforce destructional
687      * policies.
688      * <p>
689      * This method is only called by threads that have m_semaphore locked.
690      *
691      * @param poolable Poolable to be completely removed from the pool.
692      */

693     protected void removePoolable( Poolable poolable )
694     {
695         try
696         {
697             m_factory.decommission( poolable );
698
699             // Notify the InstrumentManager
700
m_decommissionsInstrument.increment();
701             if( m_sizeInstrument.isActive() )
702             {
703                 m_sizeInstrument.setValue( getSize() );
704             }
705         }
706         catch( Exception JavaDoc e )
707         {
708             if( getLogger().isDebugEnabled() )
709             {
710                 getLogger().debug( "Error decommissioning object", e );
711             }
712         }
713     }
714
715     /**
716      * Forces the pool to trim, remove, old Poolables from the pool. If the Pool
717      * was created with a non-zero value for trimInterval, then this method will
718      * be called at that interval when get() is called. If get() is not called
719      * for long periods of time then if may be necessary to call this method
720      * manually.
721      * <p>
722      * Trimming is done by maintaining two lists of objects. The first is a ready list
723      * of new poolables. The second is a list of old poolables. Each time trim() is
724      * called, the contents of the old list are removed from the pool. Then the
725      * contents of the new list is moved into the old list.
726      * <p>
727      * Each time get() is called on the pool, the new list is checked first, then the
728      * old list is checked, finally a new poolable may be created if both lists are
729      * empty. Then whenever put() is called, the poolables are always returned to
730      * the new list. In this way, the need for maining time stamps for each poolable
731      * can be avoided while at the same time avoiding unnecessary removal and creation
732      * on poolables.
733      * <p>
734      * This works out to a poolable having a maximum idle time of two calls to trim() or
735      * twice the value of trimInterval.
736      * <p>
737      * NOTE - The trimming feature does not harm performance because pools with high
738      * load will not have old poolables to be trimmed, and the benefits to system
739      * resources from not keeping around unused poolables makes up for any hit.
740      *
741      * @return the number of Poolables that were trimmed.
742      */

743     public int trim()
744     {
745         if( m_oldReady != null )
746         {
747             synchronized( m_semaphore )
748             {
749                 return trimInner();
750             }
751         }
752         else
753         {
754             throw new IllegalStateException JavaDoc( "This pool is not configured to do trimming." );
755         }
756     }
757
758     /**
759      * See trim() for details.
760      *
761      * This method is only called by threads that have m_semaphore locked.
762      */

763     private int trimInner()
764     {
765         int trimCount = 0;
766
767         // Remove any poolables in the m_oldReady list.
768
if( m_oldReadySize > 0 )
769         {
770             if( getLogger().isDebugEnabled() )
771             {
772                 getLogger().debug( "Trimming " + m_oldReadySize + " idle objects from pool." );
773             }
774
775             trimCount = m_oldReadySize;
776
777             for( Iterator JavaDoc iter = m_oldReady.iterator(); iter.hasNext(); )
778             {
779                 Poolable poolable = (Poolable)iter.next();
780                 iter.remove();
781                 m_oldReadySize--;
782                 permanentlyRemovePoolable( poolable );
783             }
784         }
785
786         // Move the poolables in m_ready into m_oldReady (swap lists)
787
if( getLogger().isDebugEnabled() )
788         {
789             getLogger().debug( "Marking " + m_readySize + " objects as old in pool." );
790         }
791         LinkedList JavaDoc tempList = m_oldReady;
792         m_oldReady = m_ready;
793         m_oldReadySize = m_readySize;
794         m_ready = tempList;
795         m_readySize = 0;
796
797         m_lastTrim = System.currentTimeMillis();
798
799         return trimCount;
800     }
801 }
802
Popular Tags