001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019: package org.openharmonise.commons.pool;
020:
021: import java.util.*;
022: import java.util.logging.*;
023: import java.util.logging.Logger;
024:
025: /**
026: * Abstract class providing base functionality for managing a pool of objects.
027: *
028: * @author Michael Bell
029: * @version $Revision: 1.1 $
030: *
031: */
032: public abstract class AbstractPool {
033:
034: /**
035: * Object expiration time
036: */
037: private long expirationTime;
038:
039: /**
040: * Last check out time
041: */
042: private long lastCheckOut;
043:
044: /**
045: * List of locked objects
046: */
047: private Map lockedMap;
048:
049: /**
050: * List of available unlocked objects
051: */
052: private Map unlockedMap;
053:
054: /**
055: * Clean-up thread
056: */
057: private CleanUpThread cleaner;
058:
059: /**
060: * Minimum number in pool
061: */
062: private int MIN_POOL = 0;
063:
064: /**
065: * Logger for logging comments
066: */
067: private static Logger m_logger = Logger
068: .getLogger(AbstractPool.class.getName());
069:
070: /**
071: * Constructs pool object with default settings
072: */
073: public AbstractPool() {
074: expirationTime = (1000 * 300000); // 30 seconds
075: lockedMap = new Hashtable();
076: unlockedMap = new Hashtable();
077: lastCheckOut = System.currentTimeMillis();
078:
079: cleaner = new CleanUpThread(this , expirationTime);
080: cleaner.start();
081:
082: //initialise pool with min objects
083: Object o;
084:
085: try {
086: for (int i = 0; i < MIN_POOL; i++) {
087: o = create();
088:
089: unlockedMap.put(o, new TimestampedObject(o, new Long(
090: System.currentTimeMillis())));
091: }
092: } catch (Exception e) {
093: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
094: }
095: }
096:
097: /**
098: * Creates new object for pool.
099: *
100: * @return object to be pooled
101: * @throws Exception if any errors occur
102: */
103: abstract public Object create() throws Exception;
104:
105: /**
106: * Returns <code>true</code> if object is valid.
107: *
108: * @param o object to be validated
109: * @return <code>true</code> of object is valid
110: */
111: abstract public boolean validate(Object o);
112:
113: /**
114: * Expires given object.
115: *
116: * @param o object to be expired
117: */
118: abstract public void expire(Object o);
119:
120: /**
121: * Sets the minimum pool size.
122: *
123: * @param num desired minimum pool size
124: */
125: public void setMinPoolSize(int num) {
126: MIN_POOL = num;
127:
128: ensureMinPool();
129: }
130:
131: /**
132: * Returns the number of objects in the pool.
133: *
134: * @return the number of objects in the pool
135: */
136: public synchronized int countObjects() {
137: return lockedMap.size() + unlockedMap.size();
138: }
139:
140: /**
141: * Checkout an object from the pool.
142: *
143: * @return a pooled object
144: * @throws Exception if any errors occur
145: */
146: public synchronized Object checkOut() throws Exception {
147: long now = System.currentTimeMillis();
148: lastCheckOut = now;
149:
150: Object o;
151: TimestampedObject to;
152: synchronized (unlockedMap) {
153: synchronized (lockedMap) {
154: if (unlockedMap.size() > 0) {
155: boolean bFoundValid = false;
156:
157: while ((bFoundValid == false)
158: && (unlockedMap.size() > 0)) {
159: o = unlockedMap.keySet().iterator().next();
160:
161: if (validate(o)) {
162: bFoundValid = true;
163: lockedMap.put(o, unlockedMap.get(o));
164: unlockedMap.remove(o);
165:
166: m_logger.logp(Level.FINER, this .getClass()
167: .getName(), "checkOut",
168: "Giving out object - "
169: + o.hashCode());
170:
171: return o;
172: } else {
173: unlockedMap.remove(o);
174: expire(o);
175: to = null;
176: }
177: }
178: }
179: o = create();
180: lockedMap.put(o,
181: new TimestampedObject(o, new Long(now)));
182: }
183: }
184:
185: ensureMinPool();
186:
187: return o;
188: }
189:
190: /**
191: * Checks the given object back in to the pool.
192: *
193: * @param o object to be checked in
194: */
195: public synchronized void checkIn(Object o) {
196: if (o != null) {
197: synchronized (unlockedMap) {
198: synchronized (lockedMap) {
199:
200: m_logger.log(Level.FINER, "Checking in..."
201: + o.hashCode());
202:
203: unlockedMap.put(o, lockedMap.get(o));
204: lockedMap.remove(o);
205: }
206: }
207: }
208: }
209:
210: /**
211: * Cleans up expired objects from the pool.
212: */
213: synchronized void cleanUp() {
214: synchronized (unlockedMap) {
215: TimestampedObject to;
216: Object o;
217: ArrayList toRemove = new ArrayList();
218:
219: long now = System.currentTimeMillis();
220:
221: for (Iterator iter = unlockedMap.keySet().iterator(); iter
222: .hasNext();) {
223: o = (Object) iter.next();
224:
225: to = (TimestampedObject) unlockedMap.get(o);
226:
227: if ((now - to.getTimestamp().longValue()) > expirationTime) {
228: //unlocked.remove(unlocked.indexOf(to));
229: toRemove.add(o);
230: expire(o);
231: }
232: }
233:
234: for (Iterator iter = toRemove.iterator(); iter.hasNext();) {
235: Object obj = (Object) iter.next();
236: unlockedMap.remove(obj);
237: }
238: }
239:
240: ensureMinPool();
241: }
242:
243: /**
244: * Ensures there is a minimum number of valid objects in the pool.
245: *
246: */
247: private void ensureMinPool() {
248: Object o;
249: synchronized (unlockedMap) {
250: if ((unlockedMap.size() + lockedMap.size()) < MIN_POOL) {
251: int num2add = MIN_POOL - unlockedMap.size();
252:
253: try {
254: for (int i = 0; i < num2add; i++) {
255: o = create();
256:
257: unlockedMap.put(o, new TimestampedObject(o,
258: new Long(System.currentTimeMillis())));
259:
260: }
261: } catch (Exception e) {
262: m_logger.log(Level.WARNING,
263: e.getLocalizedMessage(), e);
264: }
265: }
266: }
267: }
268:
269: /**
270: * Class to wrap up a pooled object which has a timestamp attached to
271: * it.
272: *
273: * @author Michael Bell
274: * @version $Revision: 1.1 $
275: *
276: */
277: private class TimestampedObject {
278: /**
279: * Object to be timestamp
280: */
281: private Object m_object;
282: /**
283: * Timestamp of object
284: */
285: private Long m_timestamp;
286:
287: /**
288: * Constructs time stamped object with given parameters
289: *
290: * @param obj the object to be timestamped
291: * @param timestamp the timestamp of the object
292: */
293: public TimestampedObject(Object obj, Long timestamp) {
294: m_timestamp = timestamp;
295: m_object = obj;
296: }
297:
298: /**
299: * Returns the object
300: *
301: * @return the object
302: */
303: public Object getObject() {
304: return m_object;
305: }
306:
307: /**
308: * Returns the timestamp associated to the object
309: *
310: * @return the timestamp
311: */
312: public Long getTimestamp() {
313: return m_timestamp;
314: }
315:
316: /* (non-Javadoc)
317: * @see java.lang.Object#equals(java.lang.Object)
318: */
319: public boolean equals(Object obj) {
320: TimestampedObject to = (TimestampedObject) obj;
321:
322: boolean bReturn = m_object.equals(to.getObject());
323:
324: return bReturn;
325: }
326:
327: /* (non-Javadoc)
328: * @see java.lang.Object#toString()
329: */
330: public String toString() {
331: return "(" + m_object.hashCode() + "," + m_timestamp + ")";
332: }
333: }
334: }
335:
336: /**
337: * <code>Thread</code> class which controls the cleaning up of expired objects
338: * in the pool.
339: *
340: * @author Michael Bell
341: * @version $Revision: 1.1 $
342: *
343: */
344: class CleanUpThread extends Thread {
345: /**
346: * The pool to be cleaned up
347: */
348: private AbstractPool pool;
349: /**
350: * Time period to sleep for between runs
351: */
352: private long sleepTime;
353:
354: /**
355: * Constructs a clean up thread with the given parameters
356: *
357: * @param pool the pool to be cleaned up
358: * @param sleepTime the period of time between runs
359: */
360: CleanUpThread(AbstractPool pool, long sleepTime) {
361: this .pool = pool;
362: this .sleepTime = sleepTime;
363: }
364:
365: /* (non-Javadoc)
366: * @see java.lang.Runnable#run()
367: */
368: public void run() {
369: while (true) {
370: try {
371: sleep(sleepTime);
372: } catch (InterruptedException e) {
373: // ignore it
374: }
375:
376: pool.cleanUp();
377: }
378: }
379: }
|