Author: dblevins
Date: Sat Apr  3 06:20:37 2010
New Revision: 930484

URL: http://svn.apache.org/viewvc?rev=930484&view=rev
Log:
For statics and logging, improved the Pool.Supplier.discard() method so that it 
says why an instance was discarded: FULL, AGED, IDLE, FLUSHED
Updated docs on pooling options.
Provide the option to immediately replace aged instances
Apply MaxAgeOffset when refilling the pool after a flush


Modified:
    
openejb/trunk/openejb3/assembly/openejb-jetty/openejb-jetty-webapp/src/main/resources/META-INF/org.apache.openejb.jetty/service-jar.xml
    
openejb/trunk/openejb3/assembly/openejb-tomcat/openejb-tomcat-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
    
openejb/trunk/openejb3/assembly/openejb-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
    
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainerFactory.java
    
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
    
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/Pool.java
    
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
    
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
    
openejb/trunk/openejb3/container/openejb-core/src/main/resources/default.openejb.conf
    
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java
    
openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml

Modified: 
openejb/trunk/openejb3/assembly/openejb-jetty/openejb-jetty-webapp/src/main/resources/META-INF/org.apache.openejb.jetty/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/assembly/openejb-jetty/openejb-jetty-webapp/src/main/resources/META-INF/org.apache.openejb.jetty/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/assembly/openejb-jetty/openejb-jetty-webapp/src/main/resources/META-INF/org.apache.openejb.jetty/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/assembly/openejb-jetty/openejb-jetty-webapp/src/main/resources/META-INF/org.apache.openejb.jetty/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -102,14 +102,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -118,6 +110,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -149,6 +158,62 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
+    # Applies to MaxAge usage and would rarely be changed, but is a
+    # nice feature to understand.
+    #
+    # When the container first starts and the pool is filled to the
+    # minimum size, all those "minimum" instances will have the same
+    # creation time and therefore all expire at the same time dictated
+    # by the MaxAge setting.  To protect against this sudden drop
+    # scenario and provide a more gradual expiration from the start
+    # the container will spread out the age of the instances that fill
+    # the pool to the minimum using an offset.
+    #
+    # The MaxAgeOffset is not the final value of the offset, but
+    # rather it is used in creating the offset and allows the
+    # spreading to push the initial ages into the future or into the
+    # past.  The pool is filled at startup as follows:
+    #
+    #  for (int i = 0; i < poolMin; i++) {
+    #    long ageOffset = (maxAge / poolMin * i * maxAgeOffset) % maxAge;
+    #    pool.add(new Bean(), ageOffset));
+    #  }
+    #
+    # The default MaxAgeOffset is -1 which causes the initial
+    # instances in the pool to live a bit longer before expiring.  As
+    # a concrete example, let's say the PoolMin is 4 and the MaxAge is
+    # 100 years.  The generated offsets for the four instances created
+    # at startup would be 0, -25, -50, -75.  So the first instance
+    # would be "born" at age 0, die at 100, living 100 years.  The
+    # second instance would be born at -25, die at 100, living a total
+    # of 125 years.  The third would live 150 years.  The fourth 175
+    # years.
+    #
+    # A MaxAgeOffset of 1 would cause instances to be "born" older
+    # and therefore die sooner.  Using the same example PoolMin of 4
+    # and MaxAge of 100 years, the life spans of these initial four
+    # instances would be 100, 75, 50, and 25 years respectively.
+    #
+    # A MaxAgeOffset of 0 will cause no "spreading" of the age of the
+    # first instances used to fill the pool to the minimum and these
+    # instances will of course reach their MaxAge at the same time.
+    # It is possible to set to decimal values such as -0.5, 0.5, -1.2,
+    # or 1.2.
+
+    MaxAgeOffset = -1
+
     # Specifies the maximum time that an instance should be allowed to
     # sit idly in the pool without use before it should be retired and
     # removed.
@@ -177,6 +242,15 @@
 
     PollInterval = 5 minutes
 
+    # When sweeping the pool for expired instances a thread pool is
+    # used to process calling @PreDestroy on expired instances as well
+    # as creating new instances as might be required to fill the pool
+    # to the minimum after a Flush or MaxAge expiration.  The
+    # CallbackThreads setting dictates the size of the thread pool and
+    # is shared by all beans deployed in the container.
+
+    CallbackThreads = 5
+
   </ServiceProvider>
 
 

Modified: 
openejb/trunk/openejb3/assembly/openejb-tomcat/openejb-tomcat-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/assembly/openejb-tomcat/openejb-tomcat-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/assembly/openejb-tomcat/openejb-tomcat-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/assembly/openejb-tomcat/openejb-tomcat-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -102,14 +102,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -118,6 +110,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -149,6 +158,62 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
+    # Applies to MaxAge usage and would rarely be changed, but is a
+    # nice feature to understand.
+    #
+    # When the container first starts and the pool is filled to the
+    # minimum size, all those "minimum" instances will have the same
+    # creation time and therefore all expire at the same time dictated
+    # by the MaxAge setting.  To protect against this sudden drop
+    # scenario and provide a more gradual expiration from the start
+    # the container will spread out the age of the instances that fill
+    # the pool to the minimum using an offset.
+    #
+    # The MaxAgeOffset is not the final value of the offset, but
+    # rather it is used in creating the offset and allows the
+    # spreading to push the initial ages into the future or into the
+    # past.  The pool is filled at startup as follows:
+    #
+    #  for (int i = 0; i &lt; poolMin; i++) {
+    #    long ageOffset = (maxAge / poolMin * i * maxAgeOffset) % maxAge;
+    #    pool.add(new Bean(), ageOffset));
+    #  }
+    #
+    # The default MaxAgeOffset is -1 which causes the initial
+    # instances in the pool to live a bit longer before expiring.  As
+    # a concrete example, let's say the PoolMin is 4 and the MaxAge is
+    # 100 years.  The generated offsets for the four instances created
+    # at startup would be 0, -25, -50, -75.  So the first instance
+    # would be "born" at age 0, die at 100, living 100 years.  The
+    # second instance would be born at -25, die at 100, living a total
+    # of 125 years.  The third would live 150 years.  The fourth 175
+    # years.
+    #
+    # A MaxAgeOffset of 1 would cause instances to be "born" older
+    # and therefore die sooner.  Using the same example PoolMin of 4
+    # and MaxAge of 100 years, the life spans of these initial four
+    # instances would be 100, 75, 50, and 25 years respectively.
+    #
+    # A MaxAgeOffset of 0 will cause no "spreading" of the age of the
+    # first instances used to fill the pool to the minimum and these
+    # instances will of course reach their MaxAge at the same time.
+    # It is possible to set to decimal values such as -0.5, 0.5, -1.2,
+    # or 1.2.
+
+    MaxAgeOffset = -1
+
     # Specifies the maximum time that an instance should be allowed to
     # sit idly in the pool without use before it should be retired and
     # removed.
@@ -177,6 +242,15 @@
 
     PollInterval = 5 minutes
 
+    # When sweeping the pool for expired instances a thread pool is
+    # used to process calling @PreDestroy on expired instances as well
+    # as creating new instances as might be required to fill the pool
+    # to the minimum after a Flush or MaxAge expiration.  The
+    # CallbackThreads setting dictates the size of the thread pool and
+    # is shared by all beans deployed in the container.
+
+    CallbackThreads = 5
+
   </ServiceProvider>
 
 

Modified: 
openejb/trunk/openejb3/assembly/openejb-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/assembly/openejb-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/assembly/openejb-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/assembly/openejb-webapp/src/main/resources/META-INF/org.apache.openejb.tomcat/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -102,14 +102,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -118,6 +110,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -149,6 +158,62 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
+    # Applies to MaxAge usage and would rarely be changed, but is a
+    # nice feature to understand.
+    #
+    # When the container first starts and the pool is filled to the
+    # minimum size, all those "minimum" instances will have the same
+    # creation time and therefore all expire at the same time dictated
+    # by the MaxAge setting.  To protect against this sudden drop
+    # scenario and provide a more gradual expiration from the start
+    # the container will spread out the age of the instances that fill
+    # the pool to the minimum using an offset.
+    #
+    # The MaxAgeOffset is not the final value of the offset, but
+    # rather it is used in creating the offset and allows the
+    # spreading to push the initial ages into the future or into the
+    # past.  The pool is filled at startup as follows:
+    #
+    #  for (int i = 0; i &lt; poolMin; i++) {
+    #    long ageOffset = (maxAge / poolMin * i * maxAgeOffset) % maxAge;
+    #    pool.add(new Bean(), ageOffset));
+    #  }
+    #
+    # The default MaxAgeOffset is -1 which causes the initial
+    # instances in the pool to live a bit longer before expiring.  As
+    # a concrete example, let's say the PoolMin is 4 and the MaxAge is
+    # 100 years.  The generated offsets for the four instances created
+    # at startup would be 0, -25, -50, -75.  So the first instance
+    # would be "born" at age 0, die at 100, living 100 years.  The
+    # second instance would be born at -25, die at 100, living a total
+    # of 125 years.  The third would live 150 years.  The fourth 175
+    # years.
+    #
+    # A MaxAgeOffset of 1 would cause instances to be "born" older
+    # and therefore die sooner.  Using the same example PoolMin of 4
+    # and MaxAge of 100 years, the life spans of these initial four
+    # instances would be 100, 75, 50, and 25 years respectively.
+    #
+    # A MaxAgeOffset of 0 will cause no "spreading" of the age of the
+    # first instances used to fill the pool to the minimum and these
+    # instances will of course reach their MaxAge at the same time.
+    # It is possible to set to decimal values such as -0.5, 0.5, -1.2,
+    # or 1.2.
+
+    MaxAgeOffset = -1
+
     # Specifies the maximum time that an instance should be allowed to
     # sit idly in the pool without use before it should be retired and
     # removed.
@@ -177,6 +242,15 @@
 
     PollInterval = 5 minutes
 
+    # When sweeping the pool for expired instances a thread pool is
+    # used to process calling @PreDestroy on expired instances as well
+    # as creating new instances as might be required to fill the pool
+    # to the minimum after a Flush or MaxAge expiration.  The
+    # CallbackThreads setting dictates the size of the thread pool and
+    # is shared by all beans deployed in the container.
+
+    CallbackThreads = 5
+
   </ServiceProvider>
 
 

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainerFactory.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainerFactory.java?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainerFactory.java
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainerFactory.java
 Sat Apr  3 06:20:37 2010
@@ -97,6 +97,14 @@ public class StatelessContainerFactory {
         pool.setPollInterval(interval);
     }
 
+    public void setReplaceAged(boolean replaceAged) {
+        pool.setReplaceAged(replaceAged);
+    }
+
+    public void setMaxAgeOffset(double maxAgeOffset) {
+        pool.setMaxAgeOffset(maxAgeOffset);
+    }
+
     public StatelessContainer create() {
         return new StatelessContainer(id, securityService, accessTimeout, 
pool, callbackThreads);
     }

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
 Sat Apr  3 06:20:37 2010
@@ -88,7 +88,7 @@ public class StatelessInstanceManager {
             this.deploymentInfo = deploymentInfo;
         }
 
-        public void discard(Instance instance) {
+        public void discard(Instance instance, Pool.Event reason) {
             ThreadContext ctx = new ThreadContext(deploymentInfo, null);
             ThreadContext oldCallContext = ThreadContext.enter(ctx);
             try {
@@ -398,9 +398,7 @@ public class StatelessInstanceManager {
 
             if (obj == null) continue;
 
-            long offset = 0;
-
-            if (maxAge > 0) offset = ((long) (maxAge / min * i * 
maxAgeOffset)) % maxAge;
+            long offset = maxAge > 0 ? ((long) (maxAge / min * i * 
maxAgeOffset)) % maxAge : 0l;
 
             data.getPool().add(obj, offset);
         }

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/Pool.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/Pool.java?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/Pool.java
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/Pool.java
 Sat Apr  3 06:20:37 2010
@@ -59,9 +59,11 @@ public class Pool<T> {
     private final AtomicInteger poolVersion = new AtomicInteger();
     private final Supplier<T> supplier;
     private final AtomicReference<Timer> timer = new AtomicReference<Timer>();
-    private Pool<T>.Eviction evictor;
-    private long interval;
-    
+    private final Sweeper sweeper;
+    private final long interval;
+    private final boolean replaceAged;
+    private double maxAgeOffset;
+
     public static class Builder<T> {
 
         private int max = 10;
@@ -73,6 +75,7 @@ public class Pool<T> {
         private Duration interval =  new Duration(5 * 60, TimeUnit.SECONDS);
         private Supplier<T> supplier;
         private Executor executor;
+        private boolean replaceAged;
 
         public Builder(Builder<T> that) {
             this.max = that.max;
@@ -84,6 +87,7 @@ public class Pool<T> {
             this.executor = that.executor;
             this.supplier = that.supplier;
             this.maxAgeOffset = that.maxAgeOffset;
+            this.replaceAged = that.replaceAged;
         }
 
         public Builder() {
@@ -93,6 +97,10 @@ public class Pool<T> {
             return min;
         }
 
+        public void setReplaceAged(boolean replaceAged) {
+            this.replaceAged = replaceAged;
+        }
+
         public void setPoolMax(int max) {
             this.max = max;
         }
@@ -147,15 +155,15 @@ public class Pool<T> {
         }
 
         public Pool<T> build() {
-            return new Pool(max, min, strict, maxAge.getTime(MILLISECONDS), 
idleTimeout.getTime(MILLISECONDS), interval.getTime(MILLISECONDS), executor, 
supplier);
+            return new Pool(max, min, strict, maxAge.getTime(MILLISECONDS), 
idleTimeout.getTime(MILLISECONDS), interval.getTime(MILLISECONDS), executor, 
supplier, false);
         }
     }
 
     public Pool(int max, int min, boolean strict) {
-        this(max, min, strict, 0, 0, 0, null, null);
+        this(max, min, strict, 0, 0, 0, null, null, false);
     }
     
-    public Pool(int max, int min, boolean strict, long maxAge, long 
idleTimeout, long interval, Executor executor, Supplier<T> supplier) {
+    public Pool(int max, int min, boolean strict, long maxAge, long 
idleTimeout, long interval, Executor executor, Supplier<T> supplier, boolean 
replaceAged) {
         if (min > max) greater("max", max, "min", min);
         if (maxAge != 0 && idleTimeout > maxAge) greater("MaxAge", maxAge, 
"IdleTimeout", idleTimeout);
         this.executor = executor != null ? executor : createExecutor();
@@ -164,16 +172,16 @@ public class Pool<T> {
         this.minimum = new Semaphore(min);
         this.instances = new Semaphore(max);
         this.maxAge = maxAge;
-
+        this.replaceAged = replaceAged;
         if (interval == 0) interval = 5 * 60 * 1000; // five minutes
         this.interval = interval;
         
-        evictor = new Eviction(idleTimeout, max);
+        sweeper = new Sweeper(idleTimeout, max);
     }
 
     public Pool start() {
         if (timer.compareAndSet(null, new Timer("PoolEviction@" + hashCode(), 
true))) {
-            timer.get().scheduleAtFixedRate(evictor, 0, this.interval);
+            timer.get().scheduleAtFixedRate(sweeper, 0, this.interval);
         }
         return this;
     }
@@ -309,7 +317,7 @@ public class Pool<T> {
             return push(new Entry<T>(obj, offset, poolVersion.get()));
         }
 
-        if (obj != null) supplier.discard(obj);
+        if (obj != null) supplier.discard(obj, Event.FULL);
         return false;
     }
 
@@ -320,6 +328,7 @@ public class Pool<T> {
     private boolean push(Entry<T> entry, boolean sweeper) {
         boolean added = false;
         boolean release = true;
+        Event event = Event.FULL;
 
         final T obj = (entry == null) ? null : entry.active.getAndSet(null);
 
@@ -334,7 +343,9 @@ public class Pool<T> {
             final boolean flushed = entry.version != this.poolVersion.get();
 
             if (aged || flushed) {
-                if (entry.hasHardReference()) {
+                if (aged) event = Event.AGED;
+                if (flushed) event = Event.FLUSHED;
+                if (entry.hasHardReference() || aged && replaceAged) {
                     // Don't release the lock, this
                     // entry will be directly replaced
                     release = false;
@@ -364,9 +375,9 @@ public class Pool<T> {
                 // if the caller is the PoolEviction thread, we do not
                 // want to be calling discard() directly and should just
                 // queue it up instead.
-                executor.execute(new Discard(obj));
+                executor.execute(new Discard(obj, event));
             } else {
-                supplier.discard(obj);
+                supplier.discard(obj, event);
             }
         }
         
@@ -495,14 +506,14 @@ public class Pool<T> {
         }
     }
 
-    private final class Eviction extends TimerTask {
+    private final class Sweeper extends TimerTask {
 
         private final AtomicInteger previousVersion = new 
AtomicInteger(poolVersion.get());
         private final long idleTimeout;
         private final boolean timeouts;
         private final int max;
 
-        private Eviction(long idleTimeout, int max) {
+        private Sweeper(long idleTimeout, int max) {
             this.idleTimeout = idleTimeout;
             timeouts = maxAge > 0 || idleTimeout > 0;
             this.max = max;
@@ -558,10 +569,10 @@ public class Pool<T> {
                         // Entry is too old, expire it
 
                         iter.remove();
-                        final Expired expired = new Expired(entry);
+                        final Expired expired = new Expired(entry, aged ? 
Event.AGED : Event.FLUSHED);
                         expiredList.add(expired);
 
-                        if (!expired.entry.hasHardReference()) {
+                        if (!expired.entry.hasHardReference() && !(aged && 
replaceAged)) {
                             expired.tryDiscard();
                         }
 
@@ -612,7 +623,7 @@ public class Pool<T> {
 
                 if (idleTimeout > 0 && idle > idleTimeout) {
                     // too lazy -- timed out 
-                    final Expired expired = new Expired(entry);
+                    final Expired expired = new Expired(entry, Event.AGED);
 
                     expiredList.add(expired);
 
@@ -629,24 +640,40 @@ public class Pool<T> {
             // If there are any "min" pool instances left over
             // we need to queue up creation of a replacement
 
+            List<Expired> replace = new ArrayList<Expired>();
+
             for (Expired expired : expiredList) {
-                executor.execute(new Discard(expired.entry.get()));
+                executor.execute(new Discard(expired.entry.get(), 
expired.event));
 
-                if (expired.entry.hasHardReference()) {
-                    executor.execute(new Replace(expired.entry));
+                if (expired.entry.hasHardReference() || expired.aged() && 
replaceAged) {
+                    replace.add(expired);
                 }
             }
 
+            for (int i = 0; i < replace.size(); i++) {
+                long offset = maxAge > 0 ? ((long) (maxAge / replace.size() * 
i * maxAgeOffset)) % maxAge : 0l;
+                executor.execute(new Replace(replace.get(i).entry, offset));
+            }
         }
 
     }
 
+    public static enum Event {
+        FULL, IDLE, AGED, FLUSHED
+    }
+
     private class Expired {
         private final Entry<T> entry;
         private final AtomicBoolean discarded = new AtomicBoolean();
+        private final Event event;
 
-        private Expired(Entry<T> entry) {
+        private Expired(Entry<T> entry, Event event) {
             this.entry = entry;
+            this.event = event;
+        }
+
+        public boolean aged() {
+            return event == Event.AGED;
         }
 
         public boolean tryDiscard() {
@@ -670,9 +697,15 @@ public class Pool<T> {
 
     private class Replace implements Runnable {
         private final Entry<T> expired;
+        private final long offset;
 
         private Replace(Entry<T> expired) {
+            this(expired, 0);
+        }
+
+        private Replace(Entry<T> expired, long offset) {
             this.expired = expired;
+            this.offset = offset;
         }
 
         public void run() {
@@ -682,8 +715,8 @@ public class Pool<T> {
                 if (t == null) {
                     discard(expired);
                 } else {
-                    final Entry entry = new Entry(t, 0 , poolVersion.get());
-                    entry.hard.set(t);
+                    final Entry entry = new Entry(t, offset, 
poolVersion.get());
+                    if (expired.hasHardReference()) entry.hard.set(t);
                     push(entry);
                 }
             } catch (Throwable e) {
@@ -696,27 +729,29 @@ public class Pool<T> {
 
     private class Discard implements Runnable {
         private final T expired;
+        private final Event event;
 
-        private Discard(T expired) {
+        private Discard(T expired, Event event) {
             if (expired == null) throw new NullPointerException("expired 
object cannot be null");
             this.expired = expired;
+            this.event = event;
         }
 
         public void run() {
-            supplier.discard(expired);
+            supplier.discard(expired, event);
         }
     }
 
     public static interface Supplier<T> {
 
-        void discard(T t);
+        void discard(T t, Event reason);
 
         T create();
 
     }
 
     private static class NoSupplier implements Supplier {
-        public void discard(Object o) {
+        public void discard(Object o, Event reason) {
         }
 
         public Object create() {

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -95,14 +95,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -111,6 +103,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -142,6 +151,62 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
+    # Applies to MaxAge usage and would rarely be changed, but is a
+    # nice feature to understand.
+    #
+    # When the container first starts and the pool is filled to the
+    # minimum size, all those "minimum" instances will have the same
+    # creation time and therefore all expire at the same time dictated
+    # by the MaxAge setting.  To protect against this sudden drop
+    # scenario and provide a more gradual expiration from the start
+    # the container will spread out the age of the instances that fill
+    # the pool to the minimum using an offset.
+    #
+    # The MaxAgeOffset is not the final value of the offset, but
+    # rather it is used in creating the offset and allows the
+    # spreading to push the initial ages into the future or into the
+    # past.  The pool is filled at startup as follows:
+    #
+    #  for (int i = 0; i &lt; poolMin; i++) {
+    #    long ageOffset = (maxAge / poolMin * i * maxAgeOffset) % maxAge;
+    #    pool.add(new Bean(), ageOffset));
+    #  }
+    #
+    # The default MaxAgeOffset is -1 which causes the initial
+    # instances in the pool to live a bit longer before expiring.  As
+    # a concrete example, let's say the PoolMin is 4 and the MaxAge is
+    # 100 years.  The generated offsets for the four instances created
+    # at startup would be 0, -25, -50, -75.  So the first instance
+    # would be "born" at age 0, die at 100, living 100 years.  The
+    # second instance would be born at -25, die at 100, living a total
+    # of 125 years.  The third would live 150 years.  The fourth 175
+    # years.
+    #
+    # A MaxAgeOffset of 1 would cause instances to be "born" older
+    # and therefore die sooner.  Using the same example PoolMin of 4
+    # and MaxAge of 100 years, the life spans of these initial four
+    # instances would be 100, 75, 50, and 25 years respectively.
+    #
+    # A MaxAgeOffset of 0 will cause no "spreading" of the age of the
+    # first instances used to fill the pool to the minimum and these
+    # instances will of course reach their MaxAge at the same time.
+    # It is possible to set to decimal values such as -0.5, 0.5, -1.2,
+    # or 1.2.
+
+    MaxAgeOffset = -1
+
     # Specifies the maximum time that an instance should be allowed to
     # sit idly in the pool without use before it should be retired and
     # removed.
@@ -170,6 +235,15 @@
 
     PollInterval = 5 minutes
 
+    # When sweeping the pool for expired instances a thread pool is
+    # used to process calling @PreDestroy on expired instances as well
+    # as creating new instances as might be required to fill the pool
+    # to the minimum after a Flush or MaxAge expiration.  The
+    # CallbackThreads setting dictates the size of the thread pool and
+    # is shared by all beans deployed in the container.
+
+    CallbackThreads = 5
+
   </ServiceProvider>
 
   <!--

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -97,14 +97,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -113,6 +105,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+    
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -144,6 +153,18 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
     # Applies to MaxAge usage and would rarely be changed, but is a
     # nice feature to understand.
     #

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/default.openejb.conf
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/default.openejb.conf?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/default.openejb.conf
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/resources/default.openejb.conf
 Sat Apr  3 06:20:37 2010
@@ -77,22 +77,31 @@
 
   AccessTimeout = 0 milliseconds
 
+  # Specifies the size of the bean pools for this stateless
+  # SessionBean container.  If StrictPooling is not used, instances
+  # will still be created beyond this number if there is demand, but
+  # they will not be returned to the pool and instead will be
+  # immediately destroyed.
+
+  PoolSize 10
+
   # Specifies the minimum number of bean instances that should be
   # in the pool for each bean.  Pools are prefilled to the minimum
   # on startup.  Pool "shrinking" is achived through SoftReferences
   # and natural vm garbage collection. All but the minimum are allowed
   # to be garbage collected by the VM when memory is needed.
+  #
+  # The minimum pool size is rigidly maintained.  The minimum side
+  # of the pool is not eligible for IdleTimeout, but is subject to
+  # MaxAge and flushing.  When the pool is flushed, the pool is
+  # immediately refilled to the minimum size with MaxAgeOffset
+  # applied.  When an instance from the minimum side of the pool
+  # reaches it's MaxAge, it is also immediately replaced.
+  # Replacement is done in a background queue using the number of
+  # threads specified by CallbackThreads.
 
   PoolMin 0
 
-  # Specifies the maximum number of bean instances that should be
-  # in the pool for each bean. If StrictPooling is not used, instances
-  # will still be created beyond this number if there is demand, but
-  # they will not be returned to the pool and instead will be
-  # immediately destroyed.
-
-  PoolSize 10
-
   # StrictPooling tells the container what to do when the pool
   # reaches it's maximum size and there are incoming requests that
   # need instances.
@@ -140,18 +149,6 @@
 
   IdleTimeout = 0 minutes
 
-  # The frequency in which the container will sweep the pool and
-  # evict expired instances.  Eviction is how the IdleTimeout,
-  # MaxAge, and pool "flush" functionality is enforced.  Higher
-  # intervals are better.  Expired instances in use while the pool
-  # is swept will still be evicted upon return to the pool.
-  #
-  # Usable time units: nanoseconds, microsecons, milliseconds,
-  # seconds, minutes, hours, days.  Or any combination such as
-  # "1 hour and 27 minutes and 10 seconds"
-
-  PollInterval = 5 minutes
-
 </Container>
 
 

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java
 Sat Apr  3 06:20:37 2010
@@ -27,8 +27,6 @@ import static java.util.concurrent.TimeU
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.io.File;
-import java.io.IOException;
 
 /**
  * @version $Rev$ $Date$
@@ -109,7 +107,7 @@ public class PoolTest extends TestCase {
         builder.setPoolMax(0);
         builder.setStrictPooling(false);
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
             }
 
@@ -285,7 +283,7 @@ public class PoolTest extends TestCase {
         builder.setIdleTimeout(new Duration(idleTimeout, 
TimeUnit.MILLISECONDS));
         builder.setPollInterval(new Duration(sweepInterval, 
TimeUnit.MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
                 discarded.add(bean);
                 discard.countDown();
@@ -371,7 +369,7 @@ public class PoolTest extends TestCase {
         builder.setPoolMax(max);
         builder.setPollInterval(new Duration(sweepInterval, 
TimeUnit.MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
                 discarded.add(bean);
                 discard.countDown();
@@ -507,7 +505,7 @@ public class PoolTest extends TestCase {
         builder.setMaxAge(new Duration(maxAge, MILLISECONDS));
         builder.setPollInterval(new Duration(sweepInterval, MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
                 discarded.add(bean);
                 countDown(discard, bean, "discarded");
@@ -659,7 +657,7 @@ public class PoolTest extends TestCase {
         builder.setPoolMax(max);
         builder.setPollInterval(new Duration(poll, TimeUnit.MILLISECONDS));
         builder.setSupplier(new Pool.Supplier() {
-            public void discard(Object o) {
+            public void discard(Object o, Pool.Event reason) {
                 discarded.countDown();
             }
 
@@ -816,7 +814,7 @@ public class PoolTest extends TestCase {
         builder.setMaxAge(new Duration(maxAge, MILLISECONDS));
         builder.setPollInterval(new Duration(poll, MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean o) {
+            public void discard(Bean o, Pool.Event reason) {
                 countDown(discarded, o, "discarded");
             }
 
@@ -976,7 +974,7 @@ public class PoolTest extends TestCase {
         builder.setPoolMax(max);
         builder.setPollInterval(new Duration(sweepInterval, 
TimeUnit.MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
             }
 
@@ -1115,7 +1113,7 @@ public class PoolTest extends TestCase {
         builder.setMaxAge(new Duration(maxAge, MILLISECONDS));
         builder.setPollInterval(new Duration(sweepInterval, MILLISECONDS));
         builder.setSupplier(new Pool.Supplier<Bean>() {
-            public void discard(Bean bean) {
+            public void discard(Bean bean, Pool.Event reason) {
                 bean.discard();
             }
 

Modified: 
openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=930484&r1=930483&r2=930484&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
 Sat Apr  3 06:20:37 2010
@@ -97,14 +97,6 @@
 
     AccessTimeout = 0 milliseconds
 
-    # Specifies the minimum number of bean instances that should be
-    # in the pool for each bean.  Pools are prefilled to the minimum
-    # on startup.  Pool "shrinking" is achived through SoftReferences
-    # and natural vm garbage collection. All but the minimum are allowed
-    # to be garbage collected by the VM when memory is needed.
-
-    PoolMin 0
-
     # Specifies the size of the bean pools for this stateless
     # SessionBean container.  If StrictPooling is not used, instances
     # will still be created beyond this number if there is demand, but
@@ -113,6 +105,23 @@
 
     PoolSize 10
 
+    # Specifies the minimum number of bean instances that should be
+    # in the pool for each bean.  Pools are prefilled to the minimum
+    # on startup.  Pool "shrinking" is achived through SoftReferences
+    # and natural vm garbage collection. All but the minimum are allowed
+    # to be garbage collected by the VM when memory is needed.
+    #
+    # The minimum pool size is rigidly maintained.  The minimum side
+    # of the pool is not eligible for IdleTimeout, but is subject to
+    # MaxAge and flushing.  When the pool is flushed, the pool is
+    # immediately refilled to the minimum size with MaxAgeOffset
+    # applied.  When an instance from the minimum side of the pool
+    # reaches it's MaxAge, it is also immediately replaced.
+    # Replacement is done in a background queue using the number of
+    # threads specified by CallbackThreads.
+
+    PoolMin 0
+
     # StrictPooling tells the container what to do when the pool
     # reaches it's maximum size and there are incoming requests that
     # need instances.
@@ -144,6 +153,62 @@
 
     MaxAge = 0 hours
 
+    # Instances from the minimum side of the pool are always replaced
+    # when they reach their MaxAge.  When this option is enabled, any
+    # instances in the pool that expire due to reaching their MaxAge
+    # will be replaced immediately so that the pool will remain at
+    # it's current size.  Replacement is done in a background queue so
+    # that incoming threads will not have to wait for instance
+    # creation.  The aim of his option is to prevent user requests
+    # from paying the instance creation cost as MaxAge is enforced,
+    # potentially while under heavy load.
+
+    ReplaceAged = true
+
+    # Applies to MaxAge usage and would rarely be changed, but is a
+    # nice feature to understand.
+    #
+    # When the container first starts and the pool is filled to the
+    # minimum size, all those "minimum" instances will have the same
+    # creation time and therefore all expire at the same time dictated
+    # by the MaxAge setting.  To protect against this sudden drop
+    # scenario and provide a more gradual expiration from the start
+    # the container will spread out the age of the instances that fill
+    # the pool to the minimum using an offset.
+    #
+    # The MaxAgeOffset is not the final value of the offset, but
+    # rather it is used in creating the offset and allows the
+    # spreading to push the initial ages into the future or into the
+    # past.  The pool is filled at startup as follows:
+    #
+    #  for (int i = 0; i &lt; poolMin; i++) {
+    #    long ageOffset = (maxAge / poolMin * i * maxAgeOffset) % maxAge;
+    #    pool.add(new Bean(), ageOffset));
+    #  }
+    #
+    # The default MaxAgeOffset is -1 which causes the initial
+    # instances in the pool to live a bit longer before expiring.  As
+    # a concrete example, let's say the PoolMin is 4 and the MaxAge is
+    # 100 years.  The generated offsets for the four instances created
+    # at startup would be 0, -25, -50, -75.  So the first instance
+    # would be "born" at age 0, die at 100, living 100 years.  The
+    # second instance would be born at -25, die at 100, living a total
+    # of 125 years.  The third would live 150 years.  The fourth 175
+    # years.
+    #
+    # A MaxAgeOffset of 1 would cause instances to be "born" older
+    # and therefore die sooner.  Using the same example PoolMin of 4
+    # and MaxAge of 100 years, the life spans of these initial four
+    # instances would be 100, 75, 50, and 25 years respectively.
+    #
+    # A MaxAgeOffset of 0 will cause no "spreading" of the age of the
+    # first instances used to fill the pool to the minimum and these
+    # instances will of course reach their MaxAge at the same time.
+    # It is possible to set to decimal values such as -0.5, 0.5, -1.2,
+    # or 1.2.
+
+    MaxAgeOffset = -1
+
     # Specifies the maximum time that an instance should be allowed to
     # sit idly in the pool without use before it should be retired and
     # removed.
@@ -172,6 +237,15 @@
 
     PollInterval = 5 minutes
 
+    # When sweeping the pool for expired instances a thread pool is
+    # used to process calling @PreDestroy on expired instances as well
+    # as creating new instances as might be required to fill the pool
+    # to the minimum after a Flush or MaxAge expiration.  The
+    # CallbackThreads setting dictates the size of the thread pool and
+    # is shared by all beans deployed in the container.
+
+    CallbackThreads = 5
+
   </ServiceProvider>
 
 


Reply via email to