001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JPool.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.pool;
025:
026: import java.util.ArrayList;
027: import java.util.HashMap;
028: import java.util.List;
029: import java.util.Map;
030:
031: import org.ow2.easybeans.api.pool.Pool;
032: import org.ow2.easybeans.api.pool.PoolAttributes;
033: import org.ow2.easybeans.api.pool.PoolException;
034: import org.ow2.util.log.Log;
035: import org.ow2.util.log.LogFactory;
036:
037: /**
038: * Abstract pool.<br>
039: * Need to be extended to set-up the correct generic types used for BeanType and
040: * Hint.<br>
041: * @param <InstanceType> the type of the object that are managed by the pool
042: * (could be EasyBeansSLSB, etc.)
043: * @param <Clue> a clue to retrieve a specific instance in the pool
044: * @author Florent Benoit
045: */
046: public class JPool<InstanceType, Clue> implements
047: Pool<InstanceType, Clue>, PoolAttributes {
048:
049: /**
050: * Default timeout (10s) in ms.
051: */
052: private static final long DEFAULT_TIMEOUT = 10000;
053:
054: /**
055: * Default maximum of waiters (1000).
056: */
057: private static final int DEFAULT_MAX_WAITERS = 1000;
058:
059: /**
060: * High Value for no limit for the connection pool.
061: */
062: private static final int NO_LIMIT = -1;
063:
064: /**
065: * Logger used.
066: */
067: private Log logger = LogFactory.getLog(JPool.class);
068:
069: /**
070: * Factory used for delegate creating, matching, removing actions.
071: */
072: private PoolFactory<InstanceType, Clue> poolFactory = null;
073:
074: /**
075: * List of available objects in the pool.
076: */
077: private List<InstanceType> availableList = null;
078:
079: /**
080: * List of objects which are currently busy and not available from the pool.
081: */
082: private List<InstanceType> busyList = null;
083:
084: /**
085: * Information on entries managed in the pool.
086: */
087: private Map<InstanceType, PoolEntryStatistics> infoMap = null;
088:
089: /**
090: * Minimum size of the pool.
091: */
092: private int minSize = 0;
093:
094: /**
095: * Maximum size of the pool.
096: */
097: private int maxSize = NO_LIMIT;
098:
099: /**
100: * Initial size of the pool.
101: */
102: private int initSize = 0;
103:
104: /**
105: * Maximum size of the pool is strict.
106: */
107: private boolean strict = true;
108:
109: /**
110: * Max nb of waiters allowed to wait for an instance.
111: */
112: private int maxWaiters = DEFAULT_MAX_WAITERS;
113:
114: /**
115: * Max nb of milliseconds to wait for an object when the pool is full.
116: */
117: private long waiterTimeout = DEFAULT_TIMEOUT;
118:
119: /**
120: * Current number of waiters.
121: */
122: private int currentWaiters = 0;
123:
124: /**
125: * Maximum number of waiters during current period.
126: */
127: private int highMaxWaiters = 0;
128:
129: /**
130: * Total nb of waiters since pool creation.
131: */
132: private int totalWaiters = 0;
133:
134: /**
135: * total waiting time in milliseconds.
136: */
137: private long totalWaitingTime = 0;
138:
139: /**
140: * Maximum waiting time during current period.
141: */
142: private long highWaitingTime = 0;
143:
144: /**
145: * Total number of instances since the pool creation.
146: */
147: private int servedInstance = 0;
148:
149: /**
150: * Total nb of instance not served due to timeout.
151: */
152: private int rejectedTimeout = 0;
153:
154: /**
155: * Total nb of instance not served due to an overflow of waiters.
156: */
157: private int rejectedFull = 0;
158:
159: /**
160: * Allow to build instance with the same clue if the pool is not at the max size.
161: * For example, for stateful this is not allowed.
162: */
163: private boolean allowSharedInstance = true;
164:
165: /**
166: * Builds a new pool.
167: * @param poolFactory factory used for delegating create, remove, etc.
168: */
169: public JPool(final PoolFactory<InstanceType, Clue> poolFactory) {
170:
171: // factory use to delegate some operations
172: this .poolFactory = poolFactory;
173:
174: // available entries
175: this .availableList = new ArrayList<InstanceType>();
176:
177: // entries send to client, which are busy
178: this .busyList = new ArrayList<InstanceType>();
179:
180: // statistics on entries
181: infoMap = new HashMap<InstanceType, PoolEntryStatistics>();
182: }
183:
184: /**
185: * Gets an object from the pool.
186: * @return an instance of an object with type Type.
187: * @throws PoolException if instance cannot be returned.
188: */
189: public InstanceType get() throws PoolException {
190: // delegates to getter method with a parameter
191: return this .get(null);
192: }
193:
194: /**
195: * Gets an object by using a specific hint.
196: * @param clue attribute used to retrieve a given instance
197: * @return a specific instance of the resource.
198: * @throws PoolException if instance cannot be returned.
199: */
200: public synchronized InstanceType get(final Clue clue)
201: throws PoolException {
202: InstanceType instance = null;
203:
204: // time to wait when pool is full
205: long timeToWait = waiterTimeout;
206:
207: // Try to find an instance
208: // Enter in the big loop :)
209: while (instance == null) {
210:
211: // instances are available
212: if (!availableList.isEmpty()) {
213: logger.debug("Instances are available");
214:
215: int indexToExtract = -1;
216:
217: // No clue, get first instance available
218: if (clue == null) {
219: logger
220: .debug("No clue, return the first available entry.");
221: indexToExtract = 0;
222: } else {
223: logger
224: .debug("Clue is used, try to get a matching entry.");
225: // need to find a matching instance.
226: // use this kind of loop to call remove() method
227: // with an index instead of an element.
228: for (int i = 0; i < availableList.size(); i++) {
229: InstanceType tmpInstance = availableList.get(i);
230: if (poolFactory.isMatching(tmpInstance, clue)) {
231: logger
232: .debug(
233: "Found a matching instance, instance = {0}",
234: tmpInstance);
235: indexToExtract = i;
236: break;
237: }
238: }
239: // Note : instance could not be found
240: }
241: // An instance is available ?
242: if (indexToExtract != -1) {
243: logger
244: .debug(
245: "Remove from available instance, item with index {0}",
246: Integer.valueOf(indexToExtract));
247: instance = availableList.remove(indexToExtract);
248: busyList.add(instance);
249: }
250: }
251:
252: /**
253: * If here :
254: * <ul>
255: * <li> - No objects available in the pool</li>
256: * <li>or - No matching instance in the pool</li>
257: * </ul>
258: */
259: if (instance == null) {
260: // current size of managed entries (available and busy)
261: int curSize = availableList.size() + busyList.size();
262:
263: boolean sharedInstance = true;
264: if (!allowSharedInstance) {
265: // Search if an instance is currently used
266: for (int i = 0; i < busyList.size(); i++) {
267: InstanceType tmpInstance = busyList.get(i);
268: if (poolFactory.isMatching(tmpInstance, clue)) {
269: logger
270: .debug(
271: "Found a matching instance, instance = {0}",
272: tmpInstance);
273: sharedInstance = false;
274: break;
275: }
276: }
277: }
278:
279: // limit not reached (or pool without limit)
280: if (sharedInstance
281: && (maxSize == NO_LIMIT || curSize < maxSize)) {
282: logger
283: .debug(
284: "Maximum size not reached, can create a new entry with clue = {0}",
285: clue);
286: instance = poolFactory.create(clue);
287: busyList.add(instance);
288: } else if (sharedInstance && availableList.size() > 0) {
289: // limit reached but pool entries still available
290: logger
291: .debug("limit reached but entries available in the pool. Replace one by a new one.");
292:
293: // get first entry found (that will be replaced)
294: InstanceType previousInstance = availableList
295: .get(0);
296:
297: // remove it from the delegating factory.
298: // Factory is notified that the instance is being removed.
299: poolFactory.remove(previousInstance);
300:
301: // now, remove the entry from pool
302: availableList.remove(previousInstance);
303: // and from statistics
304: infoMap.remove(previousInstance);
305:
306: // As previous instance has been removed, new one can be
307: // created
308: instance = poolFactory.create(clue);
309: // this instance is now busy.
310: busyList.add(instance);
311: } else {
312: logger
313: .debug("limit reached and no available instances in the pool");
314: boolean timeoutWokenUp = true;
315: long startSleeping = 0;
316:
317: // Need to wait
318: if (timeToWait > 0) {
319: if (currentWaiters < maxWaiters) {
320: currentWaiters++;
321: // Store the maximum concurrent waiters
322: if (highMaxWaiters < currentWaiters) {
323: highMaxWaiters = currentWaiters;
324: }
325: if (startSleeping == 0) {
326: startSleeping = System
327: .currentTimeMillis();
328: logger.debug("Waiting an instance");
329: }
330: try {
331: wait(timeToWait);
332: } catch (InterruptedException ie) {
333: logger.warn(
334: "Waiter has been interrupted",
335: ie);
336: } finally {
337: currentWaiters--;
338: }
339: long sleepTime = System.currentTimeMillis()
340: - startSleeping;
341: // check if waiter has been woken up
342: timeoutWokenUp = (waiterTimeout - sleepTime <= 0);
343:
344: // increment counters
345: totalWaiters++;
346: totalWaitingTime += sleepTime;
347: if (highWaitingTime < sleepTime) {
348: highWaitingTime = sleepTime;
349: }
350: // continue if we were only notified. (no timeout)
351: if (!timeoutWokenUp) {
352: continue;
353: }
354: }
355: }
356:
357: // timeout expired and no entry is available
358: if (timeoutWokenUp && availableList.isEmpty()
359: && busyList.size() >= maxSize) {
360: if (startSleeping > 0) {
361: rejectedTimeout++;
362: logger
363: .warn("Cannot create an instance due to a timeout");
364: } else {
365: rejectedFull++;
366: logger.warn("Cannot create an instance");
367: }
368: throw new PoolException(
369: "No more instances available");
370: }
371: }
372: }
373: }
374:
375: // TODO: delegate to factory the entry
376: PoolEntryStatistics poolEntryStat = new PoolEntryStatistics();
377:
378: infoMap.put(instance, poolEntryStat);
379:
380: // printLists();
381:
382: // recomputeBusy();
383:
384: servedInstance++;
385: return instance;
386: }
387:
388: /**
389: * Puts back the instance in the pool so it can be reused.
390: * @param instance which will be put back in the pool.
391: * @throws PoolException if instance is not released.
392: */
393: public synchronized void release(final InstanceType instance)
394: throws PoolException {
395:
396: if (!busyList.contains(instance)) {
397: logger
398: .debug(
399: "Attempt to release inactive resource {0}. Probably discarded.",
400: instance);
401: return;
402: }
403: busyList.remove(instance);
404: logger.debug("Put back into the pool the instance {0}",
405: instance);
406:
407: availableList.add(instance);
408: infoMap.remove(instance);
409:
410: // Notify 1 thread waiting for that an instance is available.
411: if (currentWaiters > 0) {
412: notify();
413: }
414: }
415:
416: /**
417: * Discard the instance which is in the pool.
418: * @param instance which will be discarded.
419: * @throws PoolException if instance is not discarded
420: */
421: public synchronized void discard(final InstanceType instance)
422: throws PoolException {
423: if (!busyList.contains(instance)) {
424: throw new PoolException(
425: "Attempt to discard an inactive resource("
426: + instance + ")");
427: }
428:
429: // Notify the factory that it's going to be removed
430: poolFactory.remove(instance);
431:
432: // Remove instance from the busy & info list
433: busyList.remove(instance);
434: infoMap.remove(instance);
435:
436: // Notify 1 thread waiting that an instance has been freed
437: if (currentWaiters > 0) {
438: notify();
439: }
440:
441: }
442:
443: /**
444: * Allow to share instances (shouldn't be use in stateful case as one
445: * stateful ID is linked to one client. No Stateful with same ID are
446: * authorized).
447: * @param allowSharedInstance true if it is allowed, else false.
448: */
449: public void setAllowSharedInstance(final boolean allowSharedInstance) {
450: this .allowSharedInstance = allowSharedInstance;
451: }
452:
453: /**
454: * Return true if many instances of the same object can be created (when pool is not full).
455: * @return true if this is allowed.
456: */
457: public boolean isAllowSharedInstance() {
458: return allowSharedInstance;
459: }
460:
461: /**
462: * Start the pool.<br>
463: * It could create initial instances if specified.
464: * @throws PoolException if initialization fails
465: */
466: public void start() throws PoolException {
467:
468: }
469:
470: /**
471: * Stop this pool.
472: * @throws PoolException if destroy fails
473: */
474: public void stop() throws PoolException {
475:
476: }
477:
478: }
|