001: package org.jacorb.notification.util;
002:
003: /*
004: * JacORB - a free Java ORB
005: *
006: * Copyright (C) 1999-2004 Gerald Brose
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Library General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Library General Public License for more details.
017: *
018: * You should have received a copy of the GNU Library General Public
019: * License along with this library; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.List;
030: import java.util.Set;
031:
032: import org.apache.avalon.framework.configuration.Configurable;
033: import org.apache.avalon.framework.configuration.Configuration;
034: import org.apache.avalon.framework.configuration.ConfigurationException;
035: import org.apache.avalon.framework.logger.Logger;
036: import org.jacorb.notification.interfaces.Disposable;
037:
038: import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
039:
040: /**
041: * Abstract Base Class for Simple Pooling Mechanism. Subclasses must at least implement the method
042: * newInstance. To use a Object call lendObject. After use the Object must be returned with
043: * returnObject(Object). An Object must not be used after it has been returned to its pool!
044: *
045: * This class needs a two phase initialization: configure MUST be invoked before an instance can be used.
046: *
047: * @author Alphonse Bendt
048: * @version $Id: AbstractObjectPool.java,v 1.22 2006/06/14 11:57:54 alphonse.bendt Exp $
049: */
050:
051: public abstract class AbstractObjectPool implements Runnable,
052: Configurable {
053: public static final boolean DEBUG = false;
054:
055: /**
056: * time the cleaner thread sleeps between two cleanups
057: */
058: public static final long SLEEP = 5000L;
059:
060: public static final int LOWER_WATERMARK_DEFAULT = 5;
061:
062: public static final int SIZE_INCREASE_DEFAULT = 3;
063:
064: public static final int INITIAL_SIZE_DEFAULT = 10;
065:
066: public static final int MAXIMUM_WATERMARK_DEFAULT = 20;
067:
068: public static final int MAXIMUM_SIZE_DEFAULT = 0;
069:
070: /**
071: * non synchronized as all accessing methods are synchronized.
072: */
073: private static final List sPoolsToLookAfter = new ArrayList();
074:
075: private static AbstractObjectPool[] asArray;
076:
077: private static boolean modified = true;
078:
079: private final static AbstractObjectPool[] ARRAY_TEMPLATE = new AbstractObjectPool[0];
080:
081: private static Thread sCleanerThread;
082:
083: private static final Logger sLogger_ = LogUtil
084: .getLogger(AbstractObjectPool.class.getName());
085:
086: private static ListCleaner sListCleaner;
087:
088: private static boolean sUseListCleaner = true;
089:
090: private static AbstractObjectPool[] getAllPools() {
091: synchronized (sPoolsToLookAfter) {
092: if (modified) {
093: asArray = (AbstractObjectPool[]) sPoolsToLookAfter
094: .toArray(ARRAY_TEMPLATE);
095: modified = false;
096: }
097: }
098: return asArray;
099: }
100:
101: private static void registerPool(AbstractObjectPool pool) {
102: synchronized (sPoolsToLookAfter) {
103: sPoolsToLookAfter.add(pool);
104: modified = true;
105: startListCleaner();
106: }
107: }
108:
109: private static void deregisterPool(AbstractObjectPool pool) {
110: synchronized (sPoolsToLookAfter) {
111: sPoolsToLookAfter.remove(pool);
112: modified = true;
113: if (sPoolsToLookAfter.isEmpty()) {
114: // this cleans up the asArray_ array for the GC.
115: getAllPools();
116:
117: stopListCleaner();
118: }
119: }
120: }
121:
122: private static class ListCleaner extends Thread {
123: private AtomicBoolean active_ = new AtomicBoolean(true);
124:
125: public void setInactive() {
126: active_.set(false);
127:
128: interrupt();
129: }
130:
131: private void ensureIsActive() throws InterruptedException {
132: if (!active_.get()) {
133: throw new InterruptedException();
134: }
135: }
136:
137: public void run() {
138: try {
139: while (active_.get()) {
140: try {
141: runLoop();
142: } catch (InterruptedException e) {
143: sLogger_.info("PoolCleaner was interrupted");
144: } catch (Exception e) {
145: sLogger_.error("Error cleaning Pool", e);
146: }
147: }
148: } finally {
149: synchronized (AbstractObjectPool.class) {
150: sCleanerThread = null;
151: }
152: }
153: }
154:
155: private void runLoop() throws InterruptedException {
156: while (true) {
157: try {
158: sleep(SLEEP);
159: } catch (InterruptedException ie) {
160: // ignore here.
161: // ensureIsActive is called below to see if this Thread should
162: // still be active.
163: }
164:
165: ensureIsActive();
166:
167: Runnable[] poolsToCheck = getAllPools();
168:
169: for (int x = 0; x < poolsToCheck.length; ++x) {
170: try {
171: poolsToCheck[x].run();
172: } catch (Exception t) {
173: // should not happen
174: sLogger_.error("Error cleaning up Pool", t);
175: }
176: }
177: }
178: }
179: }
180:
181: private static ListCleaner getListCleaner() {
182: synchronized (AbstractObjectPool.class) {
183: if (sListCleaner == null) {
184: sListCleaner = new ListCleaner();
185: }
186: return sListCleaner;
187: }
188: }
189:
190: private static void stopListCleaner() {
191: synchronized (AbstractObjectPool.class) {
192: if (sCleanerThread != null) {
193: sListCleaner.setInactive();
194: }
195: }
196: }
197:
198: private static void startListCleaner() {
199: synchronized (AbstractObjectPool.class) {
200: if (sCleanerThread == null && sUseListCleaner) {
201: sCleanerThread = new Thread(getListCleaner());
202:
203: sCleanerThread.setName("ObjectPoolCleaner");
204: sCleanerThread.setPriority(Thread.MIN_PRIORITY + 1);
205: sCleanerThread.setDaemon(true);
206: sCleanerThread.start();
207: }
208: }
209: }
210:
211: private final String name_;
212:
213: private final LinkedList pool_;
214:
215: private boolean isInitialized_;
216:
217: /**
218: * Set that contains all objects that were created by this pool and are in use. Problems occured
219: * as access to this member used to be non-synchronized see
220: * news://news.gmane.org:119/200406041629.48096.Farrell_John_W@cat.com
221: */
222: private final Set active_ = Collections
223: .synchronizedSet(new WeakHashSet());
224:
225: /**
226: * lower watermark. if pool size is below that value, create sizeIncrease_ new elements.
227: */
228: private int lowerWatermark_;
229:
230: /**
231: * how many instances should the pool maximal keep. instances that are returned to a pool which
232: * size is greater than maxWatermark_ are discarded and left for the Garbage Collector.
233: */
234: private int maxWatermark_;
235:
236: /**
237: * how many instances should be created if pool size falls below lowerWatermark_.
238: */
239: private int sizeIncrease_;
240:
241: /**
242: * how many instances should be created at startup of the pool.
243: */
244: private int initialSize_;
245:
246: private int maximumSize_;
247:
248: protected final Logger logger_ = LogUtil.getLogger(getClass()
249: .getName());
250:
251: protected Configuration config_;
252:
253: public void configure(Configuration conf) {
254: config_ = conf;
255:
256: init();
257: }
258:
259: protected AbstractObjectPool(String name) {
260: this (name, LOWER_WATERMARK_DEFAULT, SIZE_INCREASE_DEFAULT,
261: INITIAL_SIZE_DEFAULT, MAXIMUM_WATERMARK_DEFAULT,
262: MAXIMUM_SIZE_DEFAULT);
263: }
264:
265: protected AbstractObjectPool(String name, int lowerWatermark,
266: int sizeincrease, int initialsize, int maxWatermark,
267: int maximumSize) {
268: if (maximumSize > 0 && initialsize > maximumSize) {
269: throw new IllegalArgumentException("InitialSize: "
270: + initialsize
271: + " may not be larger than MaximumSize: "
272: + maximumSize);
273: }
274:
275: name_ = name;
276: pool_ = new LinkedList();
277: lowerWatermark_ = lowerWatermark;
278: sizeIncrease_ = sizeincrease;
279: initialSize_ = initialsize;
280: maxWatermark_ = maxWatermark;
281: maximumSize_ = maximumSize;
282: }
283:
284: public void run() {
285: final int maxToBeCreated;
286:
287: synchronized (pool_) {
288: if (pool_.size() > lowerWatermark_) {
289: return;
290: }
291:
292: maxToBeCreated = getNumberOfCreationsAllowed();
293: }
294:
295: final int sizeIncrease = Math
296: .min(sizeIncrease_, maxToBeCreated);
297:
298: if (sizeIncrease > 0) {
299: List os = new ArrayList(sizeIncrease);
300:
301: for (int x = 0; x < sizeIncrease; ++x) {
302: Object _i = createInstance();
303:
304: os.add(_i);
305: }
306:
307: synchronized (pool_) {
308: pool_.addAll(os);
309: }
310: }
311: }
312:
313: /**
314: * check the number of instances that are allowed to be created.
315: *
316: * <b>preCondition:</b> lock pool_ must be held.
317: */
318: private int getNumberOfCreationsAllowed() {
319: final int maxToBeCreated;
320:
321: if (maximumSize_ > 0) {
322: maxToBeCreated = maximumSize_ - active_.size()
323: - pool_.size();
324: } else {
325: maxToBeCreated = Integer.MAX_VALUE;
326: }
327:
328: return maxToBeCreated;
329: }
330:
331: private Object createInstance() {
332: if (logger_.isDebugEnabled()) {
333: logger_.debug("created newInstance " + getInfo());
334: }
335: return newInstance();
336: }
337:
338: /**
339: * Initialize this Pool. An initial Number of Objects is created. Cleanup Thread is started.
340: */
341: private void init() {
342: registerPool(this );
343:
344: synchronized (pool_) {
345: if (isInitialized_) {
346: throw new IllegalStateException("Already Initialized");
347: }
348:
349: for (int x = 0; x < initialSize_; ++x) {
350: Object _i = createInstance();
351:
352: pool_.add(_i);
353: }
354:
355: isInitialized_ = true;
356: }
357: }
358:
359: /**
360: * Release this Pool.
361: */
362: public void dispose() {
363: deregisterPool(this );
364: disposeCollection(pool_);
365: pool_.clear();
366: disposeCollection(active_);
367: active_.clear();
368: }
369:
370: private void disposeCollection(Collection collection) {
371: final Iterator i = collection.iterator();
372:
373: while (i.hasNext()) {
374: final Object o = i.next();
375:
376: try {
377: Disposable disposable = (Disposable) o;
378:
379: try {
380: ((AbstractPoolable) o).setObjectPool(null);
381: } catch (ClassCastException e) {
382: // ignored
383: }
384:
385: disposable.dispose();
386: } catch (ClassCastException e) {
387: // ignored
388: }
389: }
390: }
391:
392: /**
393: * lend an object from the pool.
394: */
395: public Object lendObject() {
396: checkIsInitialized();
397:
398: Object _result = null;
399:
400: synchronized (pool_) {
401: if (!pool_.isEmpty()) {
402: _result = pool_.removeFirst();
403: }
404:
405: if (_result == null) {
406: while (!isCreationAllowed()) {
407: poolIsEmpty();
408: }
409: }
410: }
411:
412: if (_result == null) {
413: _result = createInstance();
414: }
415:
416: try {
417: ((Configurable) _result).configure(this .config_);
418: } catch (ClassCastException cce) {
419: // no worries, just don't configure
420: } catch (ConfigurationException ce) {
421: throw new RuntimeException("Could not configure instance");
422: }
423:
424: doActivateObject(_result);
425: active_.add(_result);
426:
427: return _result;
428: }
429:
430: /**
431: *
432: */
433: private void checkIsInitialized() {
434: synchronized (pool_) {
435: if (!isInitialized_) {
436: throw new IllegalStateException("Not initialized");
437: }
438: }
439: }
440:
441: /**
442: * check if it is allowed to create more instances.
443: *
444: * <b>preCondition:</b> lock pool_ must be held.
445: */
446: protected boolean isCreationAllowed() {
447: return getNumberOfCreationsAllowed() > 0;
448: }
449:
450: /**
451: *
452: */
453: protected void poolIsEmpty() {
454: throw new RuntimeException(getInfo()
455: + ": No more Elements allowed. ");
456: }
457:
458: /**
459: * return an Object to the pool.
460: */
461: public void returnObject(Object o) {
462: checkIsInitialized();
463:
464: if (active_.remove(o)) {
465: doPassivateObject(o);
466:
467: if (pool_.size() < maxWatermark_) {
468: synchronized (pool_) {
469: pool_.add(o);
470: pool_.notifyAll();
471: }
472: } else {
473: doDestroyObject(o);
474: }
475: } else {
476: throw new IllegalArgumentException("Object " + o
477: + " was not created by this pool");
478: }
479: }
480:
481: public String toString() {
482: return getInfo();
483: }
484:
485: private String getInfo() {
486: return "["
487: + name_
488: + "] Active="
489: + active_.size()
490: + " Pooled="
491: + pool_.size()
492: + " MaximumSize="
493: + ((maximumSize_ > 0) ? Integer.toString(maximumSize_)
494: : "unlimited");
495: }
496:
497: /**
498: * This method is called by the Pool to create a new Instance. Subclasses must override
499: * appropiately .
500: */
501: public abstract Object newInstance();
502:
503: /**
504: * Is called after Object is returned to pool. No Op.
505: */
506: public void doPassivateObject(Object o) {
507: // No Op
508: }
509:
510: /**
511: * Is called before Object is returned to Client (lendObject). No Op
512: */
513: public void doActivateObject(Object o) {
514: // No Op
515: }
516:
517: /**
518: * Is called if Pool is full and returned Object is discarded. No Op.
519: */
520: public void doDestroyObject(Object o) {
521: // No Op
522: }
523: }
|