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