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 }