Proposal: add collecting of statistics to pool implementations

Reasoning: I'd like to be able to get stats from a pooling
implementation to see how well the various configuration parameters are
set. For example, in the GenericObjectPool implementation, I'd like to
know how many waits occurred, how many times the pool was extended, etc.

Proposed design changes:
- Create an interface called PoolStatistics that defines operations for
obtaining the statistics values and associated labels.  Actual values
would be returned in an array of ints. Value labels would be returned in
an array of Strings. This would allow each pool implementation class to
define its own set of statistics and label them accordingly.
- Create an interface called Measurable that would be implemented by a
pool that supports reporting of statistics. This interface defines the
operation that returns an instance of PoolStatistics. Defining a
separate interface allows individual pool implementations to support or
not support reporting statistics at the choosing of the implementor.

I've attached source code for the new PoolStatistics and Measurable
interfaces.  I've also attached patches to the GenericObjectPool and
GenericObjectPoolFactory implementation classes that provide a first cut
implementation, and the TestGenericObjectPool test case. I've run the
test cases on these changes and they pass. If this proposal is accepted
in some form I'll be happy to add a first cut implementation to the
other pool implementation classes.


Steven Caswell
[EMAIL PROTECTED]


Attachment: PoolStatistics.java
Description: Binary data

Attachment: Measurable.java
Description: Binary data

Index: GenericObjectPool.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons/pool/src/java/org/apache/commons/pool/impl/GenericObjectPool.java,v
retrieving revision 1.3
diff -u -r1.3 GenericObjectPool.java
--- GenericObjectPool.java      22 Apr 2002 23:43:18 -0000      1.3
+++ GenericObjectPool.java      23 Apr 2002 21:17:03 -0000
@@ -132,6 +132,11 @@
  *    {@link PoolableObjectFactory#validateObject}
  *    method.)  Objects that fail to validate will be dropped from the pool.
  *  </li>
+ *  <li>
+ *    When {@link #setCollectStatistics <i>collectStatistics</i>} is set, the
+ *    pool will collect statistics and make these available via
+ *    {@link #getStatistics}.
+ *  </li>
  * </ul>
  * <p>
  * Optionally, one may configure the pool to examine and possibly evict objects as 
they
@@ -159,9 +164,10 @@
  * </ul>
  * @see GenericKeyedObjectPool
  * @author Rodney Waldhoff
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Steven Caswell</a>
  * @version $Id: GenericObjectPool.java,v 1.3 2002/04/22 23:43:18 rwaldhoff Exp $
  */
-public class GenericObjectPool implements ObjectPool {
+public class GenericObjectPool implements ObjectPool, Measurable {
 
     //--- public constants -------------------------------------------
 
@@ -281,6 +287,13 @@
      */
     public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 
30L;
 
+    /**
+     * The default value for {@link #getCollectStatistic}
+     * @see #getCollectStatistics
+     * @see #setCollectStatistics
+     */
+    public static final boolean DEFAULT_COLLECT_STATISTICS = false;
+    
     //--- constructors -----------------------------------------------
 
     /**
@@ -288,7 +301,7 @@
      * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to 
create, validate and destroy objects
      */
     public GenericObjectPool(PoolableObjectFactory factory) {
-        
this(factory,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -297,7 +310,7 @@
      * @param config a non-<tt>null</tt> {@link GenericObjectPool.Config} describing 
my configuration
      */
     public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config 
config) {
-        
this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+        
+this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.collectStatistics);
     }
 
     /**
@@ -306,7 +319,7 @@
      * @param maxActive the maximum number of objects that can be borrowed from me at 
one time (see {@link #setMaxActive})
      */
     public GenericObjectPool(PoolableObjectFactory factory, int maxActive) {
-        
this(factory,maxActive,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -317,7 +330,7 @@
      * @param maxWait the maximum amount of time to wait for an idle object when the 
pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} 
(otherwise ignored) (see {@link #getMaxWait})
      */
     public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
whenExhaustedAction, long maxWait) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -330,7 +343,7 @@
      * @param testOnReturn whether or not to validate objects after they are returned 
to the {@link #returnObject} method (see {@link #getTestOnReturn})
      */
     public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -342,7 +355,7 @@
      * @param maxIdle the maximum number of idle objects in my pool (see {@link 
#getMaxIdle})
      */
     public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
whenExhaustedAction, long maxWait, int maxIdle) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -356,7 +369,22 @@
      * @param testOnReturn whether or not to validate objects after they are returned 
to the {@link #returnObject} method (see {@link #getTestOnReturn})
      */
     public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
testOnReturn) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE,DEFAULT_COLLECT_STATISTICS);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to 
+create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at 
+one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see 
+{@link #getWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the 
+pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} 
+(otherwise ignored) (see {@link #getMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link 
+#getMaxIdle})
+     * @param testOnBorrow whether or not to validate objects before they are 
+returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned 
+to the {@link #returnObject} method (see {@link #getTestOnReturn})
+     * @param testWhileIdle whether or not to validate objects in the idle object 
+eviction thread, if any (see {@link #setTestWhileIdle})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
+whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
+testOnReturn, boolean testWhileIdle) {
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,testWhileIdle,DEFAULT_COLLECT_STATISTICS);
     }
 
     /**
@@ -372,8 +400,9 @@
      * @param numTestsPerEvictionRun the number of idle objects to examine per run 
within the idle object eviction thread (if any) (see {@link 
#setNumTestsPerEvictionRun})
      * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object 
can sit idle in the pool before it is eligable for evcition (see {@link 
#setMinEvictableIdleTimeMillis})
      * @param testWhileIdle whether or not to validate objects in the idle object 
eviction thread, if any (see {@link #setTestWhileIdle})
+     * @param collectStatistics whether or not to collect statistics for the pool
      */
-    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long 
minEvictableIdleTimeMillis, boolean testWhileIdle) {
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte 
+whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
+testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long 
+minEvictableIdleTimeMillis, boolean testWhileIdle, boolean collectStatistics) {
         _factory = factory;
         _maxActive = maxActive;
         switch(whenExhaustedAction) {
@@ -393,8 +422,12 @@
         _numTestsPerEvictionRun = numTestsPerEvictionRun;
         _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
         _testWhileIdle = testWhileIdle;
-
+        _collectStatistics = collectStatistics;
+        
         _pool = new CursorableLinkedList();
+        if(_collectStatistics) {
+            _statistics = new GenericObjectPoolStatistics();
+        }
         if(_timeBetweenEvictionRunsMillis > 0) {
             _evictor = new Evictor();
             Thread t = new Thread(_evictor);
@@ -691,6 +724,29 @@
     }
 
     /**
+     * When <tt>true</tt>, statistics will be collected
+     *
+     * @see #setCollectStatistics
+     * @see #getStatistics
+     */
+    public boolean getCollectStatistics() {
+        return _collectStatistics;
+    }
+
+    /**
+     * When <tt>true</tt>, statistics will be collected
+     *
+     * @see #getCollectStatistics
+     * @see #getStatistics
+     */
+    public void setCollectStatistics(boolean collectStatistics) {
+        _collectStatistics= collectStatistics;
+        if(_collectStatistics && _statistics == null) {
+            _statistics = new GenericObjectPoolStatistics();
+        }
+    }
+
+    /**
      * Sets my configuration.
      * @see GenericObjectPool.Config
      */
@@ -705,6 +761,7 @@
         setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
         setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
         setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
+        setCollectStatistics(conf.collectStatistics);
         notifyAll();
     }
 
@@ -726,16 +783,28 @@
                 if(_maxActive > 0 && _numActive < _maxActive) {
                     Object obj = _factory.makeObject();
                     pair = new ObjectTimestampPair(obj);
+                    if(_collectStatistics) {
+                        
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_CREATES]++;
+                    }
                 } else {
                     // the pool is exhausted
+                    if(_collectStatistics) {
+                        
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_EXHAUSTED]++;
+                    }
                     switch(_whenExhaustedAction) {
                         case WHEN_EXHAUSTED_GROW:
                             Object obj = _factory.makeObject();
                             pair = new ObjectTimestampPair(obj);
+                            if(_collectStatistics) {
+                                
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_CREATES]++;
+                            }
                             break;
                         case WHEN_EXHAUSTED_FAIL:
                             throw new NoSuchElementException();
                         case WHEN_EXHAUSTED_BLOCK:
+                            if(_collectStatistics) {
+                                
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_WAITS]++;
+                            }
                             try {
                                 if(_maxWait <= 0) {
                                     wait();
@@ -763,8 +832,17 @@
                     ; // ignored, we're throwing it out anyway
                 }
                 _factory.destroyObject(pair.value);
+                if(_collectStatistics) {
+                    
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                }
             } else {
                 _numActive++;
+                if(_collectStatistics) {
+                    
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_BORROWS]++;
+                    
+if(_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_MOST_ACTIVE] < 
+_numActive) {
+                        
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_MOST_ACTIVE] = 
+_numActive;
+                    }
+                }
                 return pair.value;
             }
         }
@@ -775,6 +853,9 @@
         while(it.hasNext()) {
             try {
                 _factory.destroyObject(((ObjectTimestampPair)(it.next())).value);
+                if(_collectStatistics) {
+                    
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                }
             } catch(Exception e) {
                 // ignore error, keep destroying the rest
             }
@@ -814,11 +895,19 @@
                 _pool.addFirst(new ObjectTimestampPair(obj));
             }
             notifyAll(); // _numActive has changed
+            if(_collectStatistics) {
+                
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_RETURNS]++;
+            }
         }
 
         if(shouldDestroy) {
             try {
                 _factory.destroyObject(obj);
+                synchronized(this) {
+                    if(_collectStatistics) {
+                        
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                    }
+                }
             } catch(Exception e) {
                 // ignored
             }
@@ -860,6 +949,10 @@
         return buf.toString();
     }
 
+    public PoolStatistics getStatistics() {
+        return _statistics;
+    }
+
     //--- inner classes ----------------------------------------------
 
     /**
@@ -922,6 +1015,8 @@
                                         try {
                                             cursor.remove();
                                             _factory.destroyObject(pair.value);
+                                            
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                                            
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_EVICTIONS]++;
                                         } catch(Exception e) {
                                             ; // ignored
                                         }
@@ -938,6 +1033,8 @@
                                                 ; // ignored
                                             }
                                             _factory.destroyObject(pair.value);
+                                            
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                                            
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_EVICTIONS]++;
                                         }
                                         if(active) {
                                             if(!_factory.validateObject(pair.value)) {
@@ -948,12 +1045,16 @@
                                                     ; // ignored
                                                 }
                                                 _factory.destroyObject(pair.value);
+                                                
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                                                
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_EVICTIONS]++;
                                             } else {
                                                 try {
                                                     
_factory.passivateObject(pair.value);
                                                 } catch(Exception e) {
                                                     cursor.remove();
                                                     
_factory.destroyObject(pair.value);
+                                                    
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_DESTROYS]++;
+                                                    
+_statistics._statistics[GenericObjectPoolStatistics.STATISTIC_NUM_EVICTIONS]++;
                                                 }
                                             }
                                         }
@@ -997,8 +1098,76 @@
         public long timeBetweenEvictionRunsMillis = 
GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
         public int numTestsPerEvictionRun =  
GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
         public long minEvictableIdleTimeMillis = 
GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+        public boolean collectStatistics = 
+GenericObjectPool.DEFAULT_COLLECT_STATISTICS;
     }
 
+    public static class GenericObjectPoolStatistics implements PoolStatistics {
+        /**
+         * STATISTIC index for number of object borrowa
+         */
+        public static final int STATISTIC_NUM_BORROWS = 0;
+    
+        /**
+         * STATISTIC index for number of object creates
+         */
+        public static final int STATISTIC_NUM_CREATES = 1;
+    
+        /**
+         * STATISTIC index for number of waits
+         */
+        public static final int STATISTIC_NUM_WAITS = 2;
+    
+        /**
+         * STATISTIC index for number of times the pool was exhausted
+         */
+        public static final int STATISTIC_NUM_EXHAUSTED = 3;
+    
+        /**
+         * STATISTIC index for number of object evictions
+         */
+        public static final int STATISTIC_NUM_EVICTIONS = 4;
+
+        /**
+         * STATISTIC index for number of object returns
+         */
+        public static final int STATISTIC_NUM_RETURNS = 5;
+
+        /**
+         * STATISTIC index for number of object destroys
+         */
+        public static final int STATISTIC_NUM_DESTROYS = 6;
+
+        /**
+         * STATISTIC index for largest number of active objects in the pool
+         */
+        public static final int STATISTIC_MOST_ACTIVE = 7;
+        
+        /**
+         * Names of Statistics returned by {@link #getStatistics()}
+         */
+        protected static final String[] STATISTIC_NAMES = {
+            "Number of times an object was borrowed from the pool",
+            "Number of times an object was created to satisfy a borrow request",
+            "Number of times the pool waited for an object to become available to 
+satisfy a borrow request",
+            "Number of times the pool was exhausted when a borrow was requested",
+            "Number of times an object was evicted from the pool",
+            "Number of times an object was destroyed",
+            "Number of times an object was returned to the pool",
+            "Highest number of active objects in the pool"
+        };
+
+        private int _statistics[] = {0,0,0,0,0,0,0,0};
+        
+        public int[] getStatisticValues() {
+            return _statistics;
+        }
+        
+        public String[] getStatisticNames() {
+            return STATISTIC_NAMES;
+        }
+        
+    }
+    
     //--- protected attributes ---------------------------------------
 
     /**
@@ -1124,6 +1293,15 @@
      */
     protected long _minEvictableIdleTimeMillis = 
DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
 
+    /**
+     * When <tt>true</tt>, statistics will be collected
+     *
+     * @see #getCollectStatistics
+     * @see #setCollectStatistics
+     * @see #getStatistics
+     */
+    protected boolean _collectStatistics = DEFAULT_COLLECT_STATISTICS;
+
     /** My pool. */
     protected CursorableLinkedList _pool = null;
 
@@ -1140,4 +1318,9 @@
      * My idle object eviction thread, if any.
      */
     protected Evictor _evictor = null;
+
+    /**
+     * My Statistics object
+     */
+    protected GenericObjectPoolStatistics _statistics = null;
 }
Index: GenericObjectPoolFactory.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons/pool/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 GenericObjectPoolFactory.java
--- GenericObjectPoolFactory.java       14 Apr 2001 16:41:46 -0000      1.1.1.1
+++ GenericObjectPoolFactory.java       23 Apr 2002 21:17:36 -0000
@@ -70,38 +70,43 @@
  * @see ObjectPoolFactory
  *
  * @author Rodney Waldhoff
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Steven Caswell</a>
  * @version $Id: GenericObjectPoolFactory.java,v 1.1.1.1 2001/04/14 16:41:46 
rwaldhoff Exp $
  */
 public class GenericObjectPoolFactory implements ObjectPoolFactory {
     public GenericObjectPoolFactory(PoolableObjectFactory factory) {
-        
this(factory,GenericObjectPool.DEFAULT_MAX_ACTIVE,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,GenericObjectPool.DEFAULT_MAX_ACTIVE,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, 
GenericObjectPool.Config config) {
-        
this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+        
+this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.collectStatistics);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive) {
-        
this(factory,maxActive,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
byte whenExhaustedAction, long maxWait) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
byte whenExhaustedAction, long maxWait, int maxIdle) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
     public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
testOnReturn) {
-        
this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
     }
 
-    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long 
minEvictableIdleTimeMillis, boolean testWhileIdle) {
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
+byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
+testOnReturn, boolean testWhileIdle) {
+        
+this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,testWhileIdle,GenericObjectPool.DEFAULT_COLLECT_STATISTICS);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, 
+byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean 
+testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long 
+minEvictableIdleTimeMillis, boolean testWhileIdle, boolean collectStatistics) {
         _maxIdle = maxIdle;
         _maxActive = maxActive;
         _maxWait = maxWait;
@@ -112,11 +117,12 @@
         _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
         _numTestsPerEvictionRun = numTestsPerEvictionRun;
         _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _collectStatistics = collectStatistics;
         _factory = factory;
     }
 
     public ObjectPool createPool() {
-        return new 
GenericObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle);
+        return new 
+GenericObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle,_collectStatistics);
     }
 
     protected int _maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
@@ -129,6 +135,7 @@
     protected long _timeBetweenEvictionRunsMillis = 
GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
     protected int _numTestsPerEvictionRun =  
GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
     protected long _minEvictableIdleTimeMillis = 
GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    protected boolean _collectStatistics = 
+GenericObjectPool.DEFAULT_COLLECT_STATISTICS;
     protected PoolableObjectFactory _factory = null;
 
 
Index: TestGenericObjectPool.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons/pool/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java,v
retrieving revision 1.3
diff -u -r1.3 TestGenericObjectPool.java
--- TestGenericObjectPool.java  17 Mar 2002 14:55:21 -0000      1.3
+++ TestGenericObjectPool.java  23 Apr 2002 21:19:13 -0000
@@ -66,6 +66,7 @@
 
 /**
  * @author Rodney Waldhoff
+ * @author <a href="mailto:[EMAIL PROTECTED]";>Steven Caswell</a>
  * @version $Id: TestGenericObjectPool.java,v 1.3 2002/03/17 14:55:21 rwaldhoff Exp $
  */
 public class TestGenericObjectPool extends TestCase {
@@ -98,6 +99,7 @@
     }
 
     public void testBorrow() throws Exception {
+        assertEquals("pool collect statistics", false, pool.getCollectStatistics());
         Object obj0 = pool.borrowObject();
         assertEquals("0",obj0);
         Object obj1 = pool.borrowObject();
@@ -106,7 +108,20 @@
         assertEquals("2",obj2);
     }
 
+    public void testBorrowWithStatistics() throws Exception {
+        pool.setCollectStatistics(true);
+        Object obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        int expectedStatistics[] = {3, 3, 0, 0, 0, 0, 0, 3};
+        verifyStatistics(expectedStatistics);
+    }
+
     public void testBorrowReturn() throws Exception {
+        pool.setCollectStatistics(true);
         Object obj0 = pool.borrowObject();
         assertEquals("0",obj0);
         Object obj1 = pool.borrowObject();
@@ -125,6 +140,8 @@
         assertEquals("2",obj2);
         obj0 = pool.borrowObject();
         assertEquals("0",obj0);
+        int expectedStatistics[] = {7, 3, 0, 0, 0, 4, 0, 3};
+        verifyStatistics(expectedStatistics);
     }
 
     public void testNumActiveNumIdle() throws Exception {
@@ -145,6 +162,7 @@
     }
 
     public void testClear() throws Exception {
+        pool.setCollectStatistics(true);
         assertEquals(0,pool.numActive());
         assertEquals(0,pool.numIdle());
         Object obj0 = pool.borrowObject();
@@ -160,9 +178,12 @@
         assertEquals(0,pool.numIdle());
         Object obj2 = pool.borrowObject();
         assertEquals("2",obj2);
+        int expectedStatistics[] = {3, 3, 0, 0, 0, 2, 2, 2};
+        verifyStatistics(expectedStatistics);
     }
 
     public void testMaxIdle() throws Exception {
+        pool.setCollectStatistics(true);
         pool.setMaxActive(100);
         pool.setMaxIdle(8);
         Object[] active = new Object[100];
@@ -176,9 +197,12 @@
             assertEquals(99 - i,pool.numActive());
             assertEquals((i < 8 ? i+1 : 8),pool.numIdle());
         }
+        int expectedStatistics[] = {100, 100, 0, 0, 0, 100, 92, 100};
+        verifyStatistics(expectedStatistics);
     }
 
     public void testMaxActive() throws Exception {
+        pool.setCollectStatistics(true);
         pool.setMaxActive(3);
         pool.setWhenExhaustedAction(pool.WHEN_EXHAUSTED_FAIL);
 
@@ -191,9 +215,12 @@
         } catch(java.util.NoSuchElementException e) {
             // expected
         }
+        int expectedStatistics[] = {3, 3, 0, 1, 0, 0, 0, 3};
+        verifyStatistics(expectedStatistics);
     }
 
     public void testEviction() throws Exception {
+        pool.setCollectStatistics(true);
         pool.setMaxIdle(500);
         pool.setMaxActive(500);
         pool.setNumTestsPerEvictionRun(100);
@@ -240,10 +267,14 @@
         assertTrue("Should be less than 100 idle, found " + 
pool.numIdle(),pool.numIdle() < 100);
         try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
         assertEquals("Should be zero idle, found " + pool.numIdle(),0,pool.numIdle());
+
+        int expectedStatistics[] = {1000, 1000, 0, 0, 1000, 1000, 1000, 500};
+        verifyStatistics(expectedStatistics);
     }
 
 
     public void testThreaded1() throws Exception {
+        pool.setCollectStatistics(true);
         pool.setMaxActive(15);
         pool.setMaxIdle(15);
         pool.setMaxWait(1000L);
@@ -265,8 +296,20 @@
                 fail();
             }
         }
-    }
+   }
 
+    public void verifyStatistics(int[] expectedStatistics) {
+        assertEquals("pool collect statistics", true, pool.getCollectStatistics());
+        assertNotNull("pool statistics", pool.getStatistics());
+        int statistics[] = pool.getStatistics().getStatisticValues();
+        String statisticNames[] = pool.getStatistics().getStatisticNames();
+        assertEquals("statistics array length", expectedStatistics.length, 
+statistics.length);
+        assertEquals("number of borrows", 
+expectedStatistics[GenericObjectPool.GenericObjectPoolStatistics.STATISTIC_NUM_BORROWS],
+ statistics[GenericObjectPool.GenericObjectPoolStatistics.STATISTIC_NUM_BORROWS]);
+        for(int i = 0; i < statistics.length; i++) {
+            assertEquals(statisticNames[i], expectedStatistics[i], statistics[i]);
+        }
+    }
+    
     class TestThread implements Runnable {
         java.util.Random _random = new java.util.Random();
         ObjectPool _pool = null;

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to