1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.pool.impl;
  17. import java.util.Enumeration;
  18. import java.util.HashMap;
  19. import java.util.Iterator;
  20. import java.util.NoSuchElementException;
  21. import java.util.Stack;
  22. import org.apache.commons.pool.BaseKeyedObjectPool;
  23. import org.apache.commons.pool.KeyedObjectPool;
  24. import org.apache.commons.pool.KeyedPoolableObjectFactory;
  25. /**
  26. * A simple, {@link java.util.Stack Stack}-based {@link KeyedObjectPool} implementation.
  27. * <p>
  28. * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
  29. * a simple pool of instances. A finite number of "sleeping"
  30. * or inactive instances is enforced, but when the pool is
  31. * empty, new instances are created to support the new load.
  32. * Hence this class places no limit on the number of "active"
  33. * instances created by the pool, but is quite useful for
  34. * re-using <tt>Object</tt>s without introducing
  35. * artificial limits.
  36. *
  37. * @author Rodney Waldhoff
  38. * @version $Revision: 1.14 $ $Date: 2004/02/28 12:16:21 $
  39. */
  40. public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
  41. /**
  42. * Create a new pool using
  43. * no factory. Clients must first populate the pool
  44. * using {@link #returnObject(java.lang.Object,java.lang.Object)}
  45. * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
  46. */
  47. public StackKeyedObjectPool() {
  48. this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
  49. }
  50. /**
  51. * Create a new pool using
  52. * no factory. Clients must first populate the pool
  53. * using {@link #returnObject(java.lang.Object,java.lang.Object)}
  54. * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
  55. *
  56. * @param max cap on the number of "sleeping" instances in the pool
  57. */
  58. public StackKeyedObjectPool(int max) {
  59. this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
  60. }
  61. /**
  62. * Create a new pool using
  63. * no factory. Clients must first populate the pool
  64. * using {@link #returnObject(java.lang.Object,java.lang.Object)}
  65. * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
  66. *
  67. * @param max cap on the number of "sleeping" instances in the pool
  68. * @param init initial size of the pool (this specifies the size of the container,
  69. * it does not cause the pool to be pre-populated.)
  70. */
  71. public StackKeyedObjectPool(int max, int init) {
  72. this((KeyedPoolableObjectFactory)null,max,init);
  73. }
  74. /**
  75. * Create a new <tt>SimpleKeyedObjectPool</tt> using
  76. * the specified <i>factory</i> to create new instances.
  77. *
  78. * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
  79. */
  80. public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
  81. this(factory,DEFAULT_MAX_SLEEPING);
  82. }
  83. /**
  84. * Create a new <tt>SimpleKeyedObjectPool</tt> using
  85. * the specified <i>factory</i> to create new instances.
  86. * capping the number of "sleeping" instances to <i>max</i>
  87. *
  88. * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
  89. * @param max cap on the number of "sleeping" instances in the pool
  90. */
  91. public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) {
  92. this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
  93. }
  94. /**
  95. * Create a new <tt>SimpleKeyedObjectPool</tt> using
  96. * the specified <i>factory</i> to create new instances.
  97. * capping the number of "sleeping" instances to <i>max</i>,
  98. * and initially allocating a container capable of containing
  99. * at least <i>init</i> instances.
  100. *
  101. * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
  102. * @param max cap on the number of "sleeping" instances in the pool
  103. * @param init initial size of the pool (this specifies the size of the container,
  104. * it does not cause the pool to be pre-populated.)
  105. */
  106. public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
  107. _factory = factory;
  108. _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
  109. _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
  110. _pools = new HashMap();
  111. _activeCount = new HashMap();
  112. }
  113. public synchronized Object borrowObject(Object key) throws Exception {
  114. Object obj = null;
  115. Stack stack = (Stack)(_pools.get(key));
  116. if(null == stack) {
  117. stack = new Stack();
  118. stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
  119. _pools.put(key,stack);
  120. }
  121. try {
  122. obj = stack.pop();
  123. _totIdle--;
  124. } catch(Exception e) {
  125. if(null == _factory) {
  126. throw new NoSuchElementException();
  127. } else {
  128. obj = _factory.makeObject(key);
  129. }
  130. }
  131. if(null != obj && null != _factory) {
  132. _factory.activateObject(key,obj);
  133. }
  134. incrementActiveCount(key);
  135. return obj;
  136. }
  137. public synchronized void returnObject(Object key, Object obj) throws Exception {
  138. decrementActiveCount(key);
  139. if(null == _factory || _factory.validateObject(key,obj)) {
  140. Stack stack = (Stack)(_pools.get(key));
  141. if(null == stack) {
  142. stack = new Stack();
  143. stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
  144. _pools.put(key,stack);
  145. }
  146. if(null != _factory) {
  147. try {
  148. _factory.passivateObject(key,obj);
  149. } catch(Exception e) {
  150. _factory.destroyObject(key,obj);
  151. return;
  152. }
  153. }
  154. if(stack.size() < _maxSleeping) {
  155. stack.push(obj);
  156. _totIdle++;
  157. } else {
  158. if(null != _factory) {
  159. _factory.destroyObject(key,obj);
  160. }
  161. }
  162. } else {
  163. if(null != _factory) {
  164. _factory.destroyObject(key,obj);
  165. }
  166. }
  167. }
  168. public synchronized void invalidateObject(Object key, Object obj) throws Exception {
  169. decrementActiveCount(key);
  170. if(null != _factory) {
  171. _factory.destroyObject(key,obj);
  172. }
  173. notifyAll(); // _totalActive has changed
  174. }
  175. public void addObject(Object key) throws Exception {
  176. Object obj = _factory.makeObject(key);
  177. synchronized(this) {
  178. incrementActiveCount(key); // returnObject will decrement this
  179. returnObject(key,obj);
  180. }
  181. }
  182. public int getNumIdle() {
  183. return _totIdle;
  184. }
  185. public int getNumActive() {
  186. return _totActive;
  187. }
  188. public int getNumActive(Object key) {
  189. return getActiveCount(key);
  190. }
  191. public synchronized int getNumIdle(Object key) {
  192. try {
  193. return((Stack)(_pools.get(key))).size();
  194. } catch(Exception e) {
  195. return 0;
  196. }
  197. }
  198. public synchronized void clear() {
  199. Iterator it = _pools.keySet().iterator();
  200. while(it.hasNext()) {
  201. Object key = it.next();
  202. Stack stack = (Stack)(_pools.get(key));
  203. destroyStack(key,stack);
  204. }
  205. _totIdle = 0;
  206. _pools.clear();
  207. _activeCount.clear();
  208. }
  209. public synchronized void clear(Object key) {
  210. Stack stack = (Stack)(_pools.remove(key));
  211. destroyStack(key,stack);
  212. }
  213. private synchronized void destroyStack(Object key, Stack stack) {
  214. if(null == stack) {
  215. return;
  216. } else {
  217. if(null != _factory) {
  218. Enumeration enum = stack.elements();
  219. while(enum.hasMoreElements()) {
  220. try {
  221. _factory.destroyObject(key,enum.nextElement());
  222. } catch(Exception e) {
  223. // ignore error, keep destroying the rest
  224. }
  225. }
  226. }
  227. _totIdle -= stack.size();
  228. _activeCount.remove(key);
  229. stack.clear();
  230. }
  231. }
  232. public synchronized String toString() {
  233. StringBuffer buf = new StringBuffer();
  234. buf.append(getClass().getName());
  235. buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
  236. Iterator it = _pools.keySet().iterator();
  237. while(it.hasNext()) {
  238. Object key = it.next();
  239. buf.append(" |").append(key).append("|=");
  240. Stack s = (Stack)(_pools.get(key));
  241. buf.append(s.size());
  242. }
  243. return buf.toString();
  244. }
  245. public synchronized void close() throws Exception {
  246. clear();
  247. _pools = null;
  248. _factory = null;
  249. _activeCount = null;
  250. }
  251. public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
  252. if(0 < getNumActive()) {
  253. throw new IllegalStateException("Objects are already active");
  254. } else {
  255. clear();
  256. _factory = factory;
  257. }
  258. }
  259. private int getActiveCount(Object key) {
  260. try {
  261. return ((Integer)_activeCount.get(key)).intValue();
  262. } catch(NoSuchElementException e) {
  263. return 0;
  264. } catch(NullPointerException e) {
  265. return 0;
  266. }
  267. }
  268. private void incrementActiveCount(Object key) {
  269. _totActive++;
  270. Integer old = (Integer)(_activeCount.get(key));
  271. if(null == old) {
  272. _activeCount.put(key,new Integer(1));
  273. } else {
  274. _activeCount.put(key,new Integer(old.intValue() + 1));
  275. }
  276. }
  277. private void decrementActiveCount(Object key) {
  278. _totActive--;
  279. Integer active = (Integer)(_activeCount.get(key));
  280. if(null == active) {
  281. // do nothing, either null or zero is OK
  282. } else if(active.intValue() <= 1) {
  283. _activeCount.remove(key);
  284. } else {
  285. _activeCount.put(key, new Integer(active.intValue() - 1));
  286. }
  287. }
  288. /** The default cap on the number of "sleeping" instances in the pool. */
  289. protected static final int DEFAULT_MAX_SLEEPING = 8;
  290. /**
  291. * The default initial size of the pool
  292. * (this specifies the size of the container, it does not
  293. * cause the pool to be pre-populated.)
  294. */
  295. protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
  296. /** My named-set of pools. */
  297. protected HashMap _pools = null;
  298. /** My {@link KeyedPoolableObjectFactory}. */
  299. protected KeyedPoolableObjectFactory _factory = null;
  300. /** The cap on the number of "sleeping" instances in <i>each</i> pool. */
  301. protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
  302. /** The initial capacity of each pool. */
  303. protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
  304. /** Total number of object borrowed and not yet retuened for all pools */
  305. protected int _totActive = 0;
  306. /** Total number of objects "sleeping" for all pools */
  307. protected int _totIdle = 0;
  308. /** Number of active objects borrowed and not yet returned by pool */
  309. protected HashMap _activeCount = null;
  310. }