View Javadoc

1   /*
2    * ObjectPool.java
3    *
4    * This file is part of the creme library.
5    * The creme library intends to ease the development effort of large
6    * applications by providing easy to use support classes.
7    *
8    * Copyright (C) 2002 Denis Bregeon
9    *
10   *
11   * This library is free software; you can redistribute it and/or
12   * modify it under the terms of the GNU Lesser General Public
13   * License as published by the Free Software Foundation; either
14   * version 2.1 of the License, or (at your option) any later version.
15   *
16   * This library is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   * Lesser General Public License for more details.
20   *
21   * You should have received a copy of the GNU Lesser General Public
22   * License along with this library; if not, write to the Free Software
23   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24   *
25   * contact information: dbregeon@sourceforge.net
26   */
27  
28  package org.jcreme.pool;
29  
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Stack;
33  
34  /***
35   * This class represents a pool of objects. That is a number of objects that are
36   * shared in an application as a limited resource. This is a lazy
37   * implementation, it builds the pooled objects as needed through the use of the
38   * buildObject method. This class must be derived to provide an actual
39   * implementation of the buildNew method.
40   * 
41   * @author $Author: dbregeon $
42   * @version $Revision: 1.3 $
43   */
44  public abstract class ObjectPool {
45  	/***
46  	 * The initial size of the pool. It might be reused to reinitialize the
47  	 * pool.
48  	 */
49  	private int minSize = 0;
50  
51  	/***
52  	 * The maximum size of the pool.
53  	 */
54  	private int maxSize = 1;
55  
56  	/***
57  	 * The pool objects that are not currently in use.
58  	 */
59  	private final Stack freeObjects = new Stack();
60  
61  	/***
62  	 * The pool objects that are currently in use.
63  	 */
64  	private final ArrayList usedObjects = new ArrayList();
65  
66  	/***
67  	 * Creates a new instance of ObjectPool If the minSize is greater than the
68  	 * maxSize, it is reduced to the maxSize. The constructor builds as many
69  	 * objects as minSize through the initPool method.
70  	 * 
71  	 * @param minSize
72  	 *            the initial size of the pool.
73  	 * @param maxSize
74  	 *            the maximum size of the pool.
75  	 */
76  	protected ObjectPool(int minSize, int maxSize) {
77  		if (minSize > maxSize) {
78  			minSize = maxSize;
79  		}
80  		this.minSize = minSize;
81  		this.maxSize = maxSize;
82  		initPool();
83  	}
84  
85  	/***
86  	 * This method makes as many calls to buildNew as minSize. It is used to
87  	 * populate the pool.
88  	 */
89  	protected final void initPool() {
90  		Object current = null;
91  		for (int i = 0; ((i < this.minSize) && (i < this.maxSize)); i++) {
92  			current = buildNew();
93  			if (current != null) {
94  				this.freeObjects.add(current);
95  			}
96  		}
97  	}
98  
99  	/***
100 	 * This method enabled to build a new object when needed. Any parameters
101 	 * needed should be provided by the actual ObjectPool implementation.
102 	 * 
103 	 * @return a newly constructed pool element.
104 	 */
105 	protected abstract Object buildNew();
106 
107 	/***
108 	 * This method enabled to remove an old object when needed. It enables to
109 	 * free resources when an object is permanently removed from the ObjectPool.
110 	 * 
111 	 * @param obj
112 	 *            the object to remove.
113 	 */
114 	protected abstract void removeOld(final Object obj);
115 
116 	/***
117 	 * This method enables to retrieve an object from the freeObjects stack. If
118 	 * the stack is empty and the maxSize is not exceeded, it builds a new
119 	 * object using the buildNew method. If the stack is empty and the maxSize
120 	 * was reached, it waits for an object to be released. The call is then
121 	 * blocked indefinitely.
122 	 * 
123 	 * @return an object freshly removed from the freeObjects stack.
124 	 * @throws InterruptedException
125 	 *             if the wait for a free object was interrupted.
126 	 */
127 	protected Object getObjectFromFree() throws InterruptedException {
128 		Object result = null;
129 		synchronized (this.freeObjects) {
130 			if (this.freeObjects.isEmpty()) {
131 				if (this.usedObjects.size() < this.maxSize) {
132 					result = buildNew();
133 				} else {
134 					this.freeObjects.wait();
135 				}
136 			} else {
137 				result = this.freeObjects.pop();
138 			}
139 		}
140 		return result;
141 	}
142 
143 	/***
144 	 * This method enables to retrieve an object from the freeObjects stack. If
145 	 * the stack is empty and the maxSize is not exceeded, it builds a new
146 	 * object using the buildNew method. If the stack is empty and the maxSize
147 	 * was reached, it waits for an object to be released for the given time.
148 	 * 
149 	 * @param waitingPeriod
150 	 *            the time the method is allowed to wait for an object from the
151 	 *            pool.
152 	 * @return an object freshly removed from the freeObjects stack.
153 	 * @throws InterruptedException
154 	 *             if the wait for a free object was interrupted.
155 	 */
156 	protected Object getObjectFromFree(final int waitingPeriod)
157 			throws InterruptedException {
158 		Object result = null;
159 		synchronized (this.freeObjects) {
160 			if (this.freeObjects.isEmpty()) {
161 				if (this.usedObjects.size() < this.maxSize) {
162 					result = buildNew();
163 				} else if (waitingPeriod > 0) {
164 					this.freeObjects.wait(waitingPeriod);
165 				}
166 			} else {
167 				result = this.freeObjects.pop();
168 			}
169 		}
170 		return result;
171 	}
172 
173 	/***
174 	 * This method enables to remove an object from the usedObjects list.
175 	 * 
176 	 * @param o
177 	 *            the object to remove from the usedObjects.
178 	 * @return the object actually removed from the list. This is to prevent
179 	 *         objects from being introduced in the ObjectPool while they may
180 	 *         have been created outside.
181 	 */
182 	protected Object getObjectFromUsed(final Object o) {
183 		synchronized (this.usedObjects) {
184 			// Ensures that the object that is returned is really part
185 			// of the ObjectPool.
186 			return this.usedObjects.remove(this.usedObjects.indexOf(o));
187 		}
188 	}
189 
190 	/***
191 	 * This method enabled to return an object to the freeObjects stack.
192 	 * 
193 	 * @param o
194 	 *            the object to return to the stack.
195 	 */
196 	protected void putInFreeObjects(final Object o) {
197 		if (o != null) {
198 			synchronized (this.freeObjects) {
199 				this.freeObjects.push(o);
200 				this.freeObjects.notifyAll();
201 			}
202 		}
203 	}
204 
205 	/***
206 	 * This method enabled to register an object to the usedObjects list.
207 	 * 
208 	 * @param o
209 	 *            the object to add to the usedObjects list.
210 	 */
211 	protected void putInUsedObjects(final Object o) {
212 		if (o != null) {
213 			synchronized (this.usedObjects) {
214 				this.usedObjects.add(o);
215 			}
216 		}
217 	}
218 
219 	/***
220 	 * This method enables to get an object from the ObjectPool. It will block
221 	 * indefinitely while waiting for a free object.
222 	 * 
223 	 * @return an object from the ObjectPool. Null if the method was
224 	 *         interrupted.
225 	 */
226 	protected Object getObject() {
227 		Object result = null;
228 		try {
229 			while (result == null) {
230 				result = getObjectFromFree();
231 			}
232 			putInUsedObjects(result);
233 		} catch (InterruptedException e) {
234 			e.printStackTrace();
235 		}
236 		return result;
237 	}
238 
239 	/***
240 	 * This method enables to get an object from the ObjectPool. It will block
241 	 * for the given time while waiting for a free object.
242 	 * 
243 	 * @param waitingPeriod
244 	 *            the number of millisecond the method will wait for an object
245 	 *            to be freed.
246 	 * @return an object from the ObjectPool. Null if the method was
247 	 *         interrupted.
248 	 */
249 	protected Object getObject(final int waitingPeriod) {
250 		Object result = null;
251 		int loopCount = 0;
252 		int wait = waitingPeriod;
253 		try {
254 			while ((result == null) && (loopCount < 2)) {
255 				if (loopCount > 0) {
256 					wait = 0;
257 				}
258 				result = getObjectFromFree(wait);
259 				loopCount++;
260 			}
261 			putInUsedObjects(result);
262 		} catch (InterruptedException e) {
263 			e.printStackTrace();
264 		}
265 		return result;
266 	}
267 
268 	/***
269 	 * This method enables to return an object to the ObjectPool.
270 	 * 
271 	 * @param obj
272 	 *            the object to release.
273 	 */
274 	protected void releaseObject(final Object obj) {
275 		final Object pooledObject = getObjectFromUsed(obj);
276 		// return the object only if the max size is not exceeded.
277 		// this is to accomodate a change in the maxSize.
278 		if (this.usedObjects.size() + this.freeObjects.size() < this.maxSize) {
279 			putInFreeObjects(pooledObject);
280 		} else {
281 			removeOld(obj);
282 		}
283 	}
284 
285 	/***
286 	 * Gives access to the freeObjects Stack.
287 	 * 
288 	 * @return the freeObjects.
289 	 */
290 	protected Collection getFreeObjects() {
291 		return this.freeObjects;
292 	}
293 
294 	/***
295 	 * Gives access to the usedObjects Vector.
296 	 * 
297 	 * @return the usedObjects.
298 	 */
299 	protected Collection getUsedObjects() {
300 		return this.usedObjects;
301 	}
302 
303 	/***
304 	 * This method enables to change the minimum size of the ConnectionPool.
305 	 * Sizes lesser than 0 will be ignored.
306 	 * 
307 	 * @param minSize
308 	 *            the new minimum size to apply.
309 	 * 
310 	 */
311 	public void setMinimumSize(final int minSize) {
312 		if (minSize >= 0) {
313 			this.minSize = minSize;
314 			if (this.freeObjects.size() + this.usedObjects.size() < this.minSize) {
315 				synchronized (this.freeObjects) {
316 					Object current = null;
317 					for (int i = this.freeObjects.size()
318 							+ this.usedObjects.size(); ((i < this.minSize) && (i < this.maxSize)); i++) {
319 						current = buildNew();
320 
321 						if (current != null) {
322 							this.freeObjects.add(current);
323 						}
324 					}
325 				}
326 			}
327 		}
328 	}
329 
330 	/***
331 	 * This method enables to change the maximum size of the ConnectionPool. If
332 	 * the size is lower than the minimum size, it will be ignored.
333 	 * 
334 	 * @param maxSize
335 	 *            the new maximum size to apply.
336 	 * 
337 	 */
338 	public void setMaximumSize(final int maxSize) {
339 		if (maxSize >= this.minSize) {
340 			this.maxSize = maxSize;
341 			synchronized (this.freeObjects) {
342 				Object current = null;
343 				while (this.usedObjects.size() + this.freeObjects.size() > this.maxSize) {
344 					current = this.freeObjects.lastElement();
345 					this.freeObjects.remove(current);
346 					removeOld(current);
347 				}
348 			}
349 		}
350 	}
351 }