View Javadoc
1   package org.codehaus.plexus.util;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.util.ArrayList;
20  
21  /**
22   * Pools a bunch of objects . Runs a sweeper periodically to keep it down to size. The objects in the pool first get
23   * disposed first.
24   *
25   * @author <a href="mailto:bert@tuaworks.co.nz">Bert van Brakel</a>
26   * @version $Id$
27   */
28  public class SweeperPool
29  {
30      /***/
31      private static final boolean DEBUG = false;
32  
33      /** Sweeps the pool periodically to trim it's size */
34      private transient Sweeper sweeper;
35  
36      /** Absolute maximum size of the pool. */
37      private transient int maxSize;
38  
39      /** The size the pool gets trimmed down to */
40      private transient int minSize;
41  
42      /**
43       * When the sweeper runs and the pool is over this size, then the pool is trimmed
44       */
45      private int triggerSize;
46  
47      /** Holds the pooled objects */
48      private ArrayList<Object> pooledObjects;
49  
50      /** Flag indicating this pool is shuting down */
51      private boolean shuttingDown = false;
52  
53      // private Vector used;
54  
55      /**
56       * <p>
57       * There are a number of settings to control how the pool operates.
58       * <ul>
59       * <li><code>minSize</code> - this is the size the pool is trimmed to</li>
60       * <li><code>triggerSize</code> - this determines if the pool is trimmed when the sweeper runs. If the pool size is
61       * greater or equal than this value then the pool is trimmed to <code>minSize</code>.</lie>
62       * <li><code>maxSize</code> - if the pool has reached this size, any objects added are immediately disposed. If the
63       * pool is this size when the sweeper runs, then the pool is also trimmed to <code>minSize</code> irrespective of
64       * the triggerSize.</li>
65       * <li><code>sweepInterval</code> - how often the sweeper runs. Is actually the time since the sweeper last finished
66       * a pass. 0 if the sweeper should not run.</li>
67       * </ul>
68       * </p>
69       * <p>
70       * Any value less than 0 is automatically converted to 0
71       * </p>
72       */
73      public SweeperPool( int maxSize, int minSize, int intialCapacity, int sweepInterval, int triggerSize )
74      {
75          super();
76          this.maxSize = saneConvert( maxSize );
77          this.minSize = saneConvert( minSize );
78          this.triggerSize = saneConvert( triggerSize );
79          pooledObjects = new ArrayList( intialCapacity );
80  
81          // only run a sweeper if sweep interval is positive
82          if ( sweepInterval > 0 )
83          {
84              sweeper = new Sweeper( this, sweepInterval );
85              sweeper.start();
86          }
87      }
88  
89      private int saneConvert( int value )
90      {
91          if ( value < 0 )
92          {
93              return 0;
94          }
95          else
96          {
97              return value;
98          }
99      }
100 
101     /**
102      * Return the pooled object
103      */
104     public synchronized Object get()
105     {
106         if ( ( pooledObjects.size() == 0 ) || shuttingDown )
107         {
108             return null;
109         }
110         else
111         {
112             Object obj = pooledObjects.remove( 0 );
113             objectRetrieved( obj );
114 
115             // used.add(obj);
116             return obj;
117         }
118     }
119 
120     /**
121      * Add an object to the pool
122      *
123      * @param obj the object to pool. Can be null.
124      * @return true if the object was added to the pool, false if it was disposed or null
125      */
126     public synchronized boolean put( Object obj )
127     {
128         objectAdded( obj );
129 
130         if ( ( obj != null ) && ( pooledObjects.size() < maxSize ) && ( shuttingDown == false ) )
131         {
132             pooledObjects.add( obj );
133 
134             return true;
135         }
136         else if ( obj != null )
137         {
138             // no longer need the object, so dispose it
139             objectDisposed( obj );
140         }
141 
142         return false;
143     }
144 
145     /**
146      * Return the number of pooled objects. This is never greater than t maximum size of the pool
147      *
148      * @return the number of pooled objects
149      */
150     public synchronized int getSize()
151     {
152         return pooledObjects.size();
153     }
154 
155     /**
156      * Dispose of this pool. Stops the sweeper and disposes each object in the pool
157      */
158     public void dispose()
159     {
160         shuttingDown = true;
161 
162         if ( sweeper != null )
163         {
164             sweeper.stop();
165             try
166             {
167                 sweeper.join();
168             }
169             catch ( InterruptedException e )
170             {
171                 System.err.println( "Unexpected exception occurred: " );
172                 e.printStackTrace();
173             }
174         }
175 
176         synchronized ( this )
177         {
178             // use an array here as objects may still be being put back in the pool
179             // and we don't want to throw a ConcurrentModificationException
180             Object[] objects = pooledObjects.toArray();
181 
182             for ( Object object : objects )
183             {
184                 objectDisposed( object );
185             }
186 
187             pooledObjects.clear();
188         }
189     }
190 
191     /**
192      * A pool has been disposed if has been shutdown and the sweeper has completed running.
193      *
194      * @return true if the pool has been disposed, false otherwise
195      */
196     boolean isDisposed()
197     {
198         if ( !shuttingDown )
199         {
200             return false;
201         }
202 
203         // A null sweeper means one was never started.
204         if ( sweeper == null )
205         {
206             return true;
207         }
208 
209         return sweeper.hasStopped();
210     }
211 
212     /**
213      * Trim the pool down to min size
214      */
215     public synchronized void trim()
216     {
217         if ( ( ( triggerSize > 0 ) && ( pooledObjects.size() >= triggerSize ) )
218             || ( ( maxSize > 0 ) && ( pooledObjects.size() >= maxSize ) ) )
219         {
220             while ( pooledObjects.size() > minSize )
221             {
222                 objectDisposed( pooledObjects.remove( 0 ) );
223             }
224         }
225     }
226 
227     /**
228      * Override this to be notified of object disposal. Called after the object has been removed. Occurs when the pool
229      * is trimmed.
230      *
231      * @param obj
232      */
233     public void objectDisposed( Object obj )
234     {
235     }
236 
237     /**
238      * Override this to be notified of object addition. Called before object is to be added.
239      *
240      * @param obj
241      */
242     public void objectAdded( Object obj )
243     {
244     }
245 
246     /**
247      * Override this to be notified of object retrieval. Called after object removed from the pool, but before returned
248      * to the client.
249      *
250      * @param obj
251      */
252     public void objectRetrieved( Object obj )
253     {
254     }
255 
256     /**
257      * Periodically at <code>sweepInterval</code> goes through and tests if the pool should be trimmed.
258      *
259      * @author bert
260      */
261     private static class Sweeper
262         implements Runnable
263     {
264         private final transient SweeperPool pool;
265 
266         private transient boolean service = false;
267 
268         private final transient int sweepInterval;
269 
270         private transient Thread t = null;
271 
272         /**
273          *
274          */
275         public Sweeper( SweeperPool pool, int sweepInterval )
276         {
277             super();
278             this.sweepInterval = sweepInterval;
279             this.pool = pool;
280         }
281 
282         /**
283          * Run the sweeper.
284          *
285          * @see java.lang.Runnable#run()
286          */
287         public void run()
288         {
289             debug( "started" );
290 
291             if ( sweepInterval > 0 )
292             {
293                 synchronized ( this )
294                 {
295                     while ( service )
296                     {
297                         try
298                         {
299                             // wait specified number of seconds
300                             // before running next sweep
301                             wait( sweepInterval * 1000 );
302                         }
303                         catch ( InterruptedException e )
304                         {
305                         }
306                         runSweep();
307                     }
308                 }
309             }
310 
311             debug( "stopped" );
312         }
313 
314         public void start()
315         {
316             if ( !service )
317             {
318                 service = true;
319                 t = new Thread( this );
320                 t.setName( "Sweeper" );
321                 t.start();
322             }
323         }
324 
325         public synchronized void stop()
326         {
327             service = false;
328             notifyAll();
329         }
330 
331         void join()
332             throws InterruptedException
333         {
334             t.join();
335         }
336 
337         boolean hasStopped()
338         {
339             return !service && !t.isAlive();
340         }
341 
342         private final void debug( String msg )
343         {
344             if ( DEBUG )
345             {
346                 System.err.println( this + ":" + msg );
347             }
348         }
349 
350         private void runSweep()
351         {
352             debug( "runningSweep. time=" + System.currentTimeMillis() );
353             pool.trim();
354         }
355     }
356 }