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   *
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       * There are a number of settings to control how the pool operates.
57       * @param maxSize if the pool has reached this size, any objects added are immediately disposed. If the
58       *      pool is this size when the sweeper runs, then the pool is also trimmed to <code>minSize</code> irrespective of
59       *      the triggerSize.
60       * @param minSize - this is the size the pool is trimmed to
61       * @param  triggerSize - this determines if the pool is trimmed when the sweeper runs. If the pool size is
62       * greater or equal than this value then the pool is trimmed to <code>minSize</code>.
63       *
64       * @param sweepInterval how often the sweeper runs. Is actually the time since the sweeper last finished
65       * a pass. 0 if the sweeper should not run.
66       * @param intialCapacity the intial capacity
67       * <p>Any value less than 0 is automatically converted to 0</p>
68       */
69      public SweeperPool( int maxSize, int minSize, int intialCapacity, int sweepInterval, int triggerSize )
70      {
71          super();
72          this.maxSize = saneConvert( maxSize );
73          this.minSize = saneConvert( minSize );
74          this.triggerSize = saneConvert( triggerSize );
75          pooledObjects = new ArrayList( intialCapacity );
76  
77          // only run a sweeper if sweep interval is positive
78          if ( sweepInterval > 0 )
79          {
80              sweeper = new Sweeper( this, sweepInterval );
81              sweeper.start();
82          }
83      }
84  
85      private int saneConvert( int value )
86      {
87          return Math.max( value, 0 );
88      }
89  
90      /**
91       * Return the pooled object
92       * @return first available object from the pool
93       */
94      public synchronized Object get()
95      {
96          if ( ( pooledObjects.size() == 0 ) || shuttingDown )
97          {
98              return null;
99          }
100         else
101         {
102             Object obj = pooledObjects.remove( 0 );
103             objectRetrieved( obj );
104 
105             // used.add(obj);
106             return obj;
107         }
108     }
109 
110     /**
111      * Add an object to the pool
112      *
113      * @param obj the object to pool. Can be null.
114      * @return true if the object was added to the pool, false if it was disposed or null
115      */
116     public synchronized boolean put( Object obj )
117     {
118         objectAdded( obj );
119 
120         if ( ( obj != null ) && ( pooledObjects.size() < maxSize ) && ( shuttingDown == false ) )
121         {
122             pooledObjects.add( obj );
123 
124             return true;
125         }
126         else if ( obj != null )
127         {
128             // no longer need the object, so dispose it
129             objectDisposed( obj );
130         }
131 
132         return false;
133     }
134 
135     /**
136      * Return the number of pooled objects. This is never greater than t maximum size of the pool
137      *
138      * @return the number of pooled objects
139      */
140     public synchronized int getSize()
141     {
142         return pooledObjects.size();
143     }
144 
145     /**
146      * Dispose of this pool. Stops the sweeper and disposes each object in the pool
147      */
148     public void dispose()
149     {
150         shuttingDown = true;
151 
152         if ( sweeper != null )
153         {
154             sweeper.stop();
155             try
156             {
157                 sweeper.join();
158             }
159             catch ( InterruptedException e )
160             {
161                 System.err.println( "Unexpected exception occurred: " );
162                 e.printStackTrace();
163             }
164         }
165 
166         synchronized ( this )
167         {
168             // use an array here as objects may still be being put back in the pool
169             // and we don't want to throw a ConcurrentModificationException
170             Object[] objects = pooledObjects.toArray();
171 
172             for ( Object object : objects )
173             {
174                 objectDisposed( object );
175             }
176 
177             pooledObjects.clear();
178         }
179     }
180 
181     /**
182      * A pool has been disposed if has been shutdown and the sweeper has completed running.
183      *
184      * @return true if the pool has been disposed, false otherwise
185      */
186     boolean isDisposed()
187     {
188         if ( !shuttingDown )
189         {
190             return false;
191         }
192 
193         // A null sweeper means one was never started.
194         if ( sweeper == null )
195         {
196             return true;
197         }
198 
199         return sweeper.hasStopped();
200     }
201 
202     /**
203      * Trim the pool down to min size
204      */
205     public synchronized void trim()
206     {
207         if ( ( ( triggerSize > 0 ) && ( pooledObjects.size() >= triggerSize ) )
208             || ( ( maxSize > 0 ) && ( pooledObjects.size() >= maxSize ) ) )
209         {
210             while ( pooledObjects.size() > minSize )
211             {
212                 objectDisposed( pooledObjects.remove( 0 ) );
213             }
214         }
215     }
216 
217     /**
218      * Override this to be notified of object disposal. Called after the object has been removed. Occurs when the pool
219      * is trimmed.
220      *
221      * @param obj the Object
222      */
223     public void objectDisposed( Object obj )
224     {
225     }
226 
227     /**
228      * Override this to be notified of object addition. Called before object is to be added.
229      *
230      * @param obj the Object
231      */
232     public void objectAdded( Object obj )
233     {
234     }
235 
236     /**
237      * Override this to be notified of object retrieval. Called after object removed from the pool, but before returned
238      * to the client.
239      *
240      * @param obj the Object
241      */
242     public void objectRetrieved( Object obj )
243     {
244     }
245 
246     /**
247      * Periodically at <code>sweepInterval</code> goes through and tests if the pool should be trimmed.
248      *
249      * @author bert
250      */
251     private static class Sweeper
252         implements Runnable
253     {
254         private final transient SweeperPool pool;
255 
256         private transient boolean service = false;
257 
258         private final transient int sweepInterval;
259 
260         private transient Thread t = null;
261 
262         /**
263          *
264          */
265         public Sweeper( SweeperPool pool, int sweepInterval )
266         {
267             super();
268             this.sweepInterval = sweepInterval;
269             this.pool = pool;
270         }
271 
272         /**
273          * Run the sweeper.
274          *
275          * @see java.lang.Runnable#run()
276          */
277         @Override
278         public void run()
279         {
280             debug( "started" );
281 
282             if ( sweepInterval > 0 )
283             {
284                 synchronized ( this )
285                 {
286                     while ( service )
287                     {
288                         try
289                         {
290                             // wait specified number of seconds
291                             // before running next sweep
292                             wait( sweepInterval * 1000 );
293                         }
294                         catch ( InterruptedException e )
295                         {
296                         }
297                         runSweep();
298                     }
299                 }
300             }
301 
302             debug( "stopped" );
303         }
304 
305         public void start()
306         {
307             if ( !service )
308             {
309                 service = true;
310                 t = new Thread( this );
311                 t.setName( "Sweeper" );
312                 t.start();
313             }
314         }
315 
316         public synchronized void stop()
317         {
318             service = false;
319             notifyAll();
320         }
321 
322         void join()
323             throws InterruptedException
324         {
325             t.join();
326         }
327 
328         boolean hasStopped()
329         {
330             return !service && !t.isAlive();
331         }
332 
333         private final void debug( String msg )
334         {
335             if ( DEBUG )
336             {
337                 System.err.println( this + ":" + msg );
338             }
339         }
340 
341         private void runSweep()
342         {
343             debug( "runningSweep. time=" + System.currentTimeMillis() );
344             pool.trim();
345         }
346     }
347 }