You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by dj...@apache.org on 2004/11/01 18:34:15 UTC

svn commit: rev 56257 - in geronimo/trunk/modules: assembly/src/plan connector/src/java/org/apache/geronimo/connector/deployment connector/src/java/org/apache/geronimo/connector/outbound connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig connector/src/schema connector/src/test/org/apache/geronimo/connector/deployment connector/src/test/org/apache/geronimo/connector/outbound

Author: djencks
Date: Mon Nov  1 09:34:13 2004
New Revision: 56257

Added:
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractSinglePoolConnectionInterceptor.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolIdleReleaserTimer.java
Modified:
   geronimo/trunk/modules/assembly/src/plan/j2ee-deployer-plan.xml
   geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/deployment/ConnectorModuleBuilder.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/MultiPoolConnectionInterceptor.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolingAttributes.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolConnectionInterceptor.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolMatchAllConnectionInterceptor.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/NoPool.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/PartitionedPool.java
   geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/SinglePool.java
   geronimo/trunk/modules/connector/src/schema/geronimo-connector_1_5.xsd
   geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_0ConfigBuilderTest.java
   geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_5ConfigBuilderTest.java
   geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/ConnectionManagerTestUtils.java
   geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/PoolDequeTest.java
Log:
GERONIMO-412. Implement minSize and idleTimeout for connection pools, and make maxSize, minSize, blockingTimeoutMilliseconds, and idleTimeoutMinutes read-write at runtime.  Set default values from module builder configuration and make plan values optional. No significant testing yet.

Modified: geronimo/trunk/modules/assembly/src/plan/j2ee-deployer-plan.xml
==============================================================================
--- geronimo/trunk/modules/assembly/src/plan/j2ee-deployer-plan.xml	(original)
+++ geronimo/trunk/modules/assembly/src/plan/j2ee-deployer-plan.xml	Mon Nov  1 09:34:13 2004
@@ -109,7 +109,12 @@
 
     <gbean name="geronimo.deployer:role=ClientEJBReferenceBuilder,config=org/apache/geronimo/J2EEDeployer" class="org.openejb.deployment.RemoteEJBReferenceBuilder"/>
 
-    <gbean name="geronimo.deployer:role=ModuleBuilder,type=Connector,config=org/apache/geronimo/J2EEDeployer" class="org.apache.geronimo.connector.deployment.ConnectorModuleBuilder"/>
+    <gbean name="geronimo.deployer:role=ModuleBuilder,type=Connector,config=org/apache/geronimo/J2EEDeployer" class="org.apache.geronimo.connector.deployment.ConnectorModuleBuilder">
+        <attribute name="defaultMaxSize" type="int">10</attribute>
+        <attribute name="defaultMinSize" type="int">0</attribute>
+        <attribute name="defaultBlockingTimeoutMilliseconds" type="int">5000</attribute>
+        <attribute name="defaultIdleTimeoutMinutes" type="int">15</attribute>
+    </gbean>
 
     <gbean name="geronimo.deployer:role=ModuleBuilder,type=AppClient,config=org/apache/geronimo/J2EEDeployer" class="org.apache.geronimo.client.builder.AppClientModuleBuilder">
         <attribute name="transactionContextManagerObjectName" type="javax.management.ObjectName">geronimo.client:type=TransactionContextManager</attribute>

Modified: geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml
==============================================================================
--- geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml	(original)
+++ geronimo/trunk/modules/assembly/src/plan/j2ee-server-plan.xml	Mon Nov  1 09:34:13 2004
@@ -268,7 +268,12 @@
 
     <gbean name="geronimo.deployer:role=ClientEJBReferenceBuilder,config=org/apache/geronimo/Server" class="org.openejb.deployment.RemoteEJBReferenceBuilder"/>
 
-    <gbean name="geronimo.deployer:role=ModuleBuilder,type=Connector,config=org/apache/geronimo/Server" class="org.apache.geronimo.connector.deployment.ConnectorModuleBuilder"/>
+    <gbean name="geronimo.deployer:role=ModuleBuilder,type=Connector,config=org/apache/geronimo/Server" class="org.apache.geronimo.connector.deployment.ConnectorModuleBuilder">
+        <attribute name="defaultMaxSize" type="int">10</attribute>
+        <attribute name="defaultMinSize" type="int">0</attribute>
+        <attribute name="defaultBlockingTimeoutMilliseconds" type="int">5000</attribute>
+        <attribute name="defaultIdleTimeoutMinutes" type="int">15</attribute>
+    </gbean>
 
     <gbean name="geronimo.deployer:role=ModuleBuilder,type=AppClient,config=org/apache/geronimo/Server" class="org.apache.geronimo.client.builder.AppClientModuleBuilder">
         <attribute name="transactionContextManagerObjectName" type="javax.management.ObjectName">geronimo.client:type=TransactionContextManager</attribute>

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/deployment/ConnectorModuleBuilder.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/deployment/ConnectorModuleBuilder.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/deployment/ConnectorModuleBuilder.java	Mon Nov  1 09:34:13 2004
@@ -80,6 +80,8 @@
 import org.apache.geronimo.xbeans.geronimo.GerDependencyType;
 import org.apache.geronimo.xbeans.geronimo.GerGbeanType;
 import org.apache.geronimo.xbeans.geronimo.GerResourceadapterType;
+import org.apache.geronimo.xbeans.geronimo.GerSinglepoolType;
+import org.apache.geronimo.xbeans.geronimo.GerPartitionedpoolType;
 import org.apache.geronimo.xbeans.j2ee.ActivationspecType;
 import org.apache.geronimo.xbeans.j2ee.AdminobjectType;
 import org.apache.geronimo.xbeans.j2ee.ConfigPropertyType;
@@ -104,9 +106,17 @@
     private static final String BASE_REALM_BRIDGE_NAME = "geronimo.security:service=RealmBridge,name=";
     private static final String BASE_PASSWORD_CREDENTIAL_LOGIN_MODULE_NAME = "geronimo.security:service=Realm,type=PasswordCredential,name=";
 
+    private final int defaultMaxSize;
+    private final int defaultMinSize;
+    private final int defaultBlockingTimeoutMilliseconds;
+    private final int defaultIdleTimeoutMinutes;
     private final Kernel kernel;
 
-    public ConnectorModuleBuilder(Kernel kernel) {
+    public ConnectorModuleBuilder(int defaultMaxSize, int defaultMinSize, int defaultBlockingTimeoutMilliseconds, int defaultIdleTimeoutMinutes, Kernel kernel) {
+        this.defaultMaxSize = defaultMaxSize;
+        this.defaultMinSize = defaultMinSize;
+        this.defaultBlockingTimeoutMilliseconds = defaultBlockingTimeoutMilliseconds;
+        this.defaultIdleTimeoutMinutes = defaultIdleTimeoutMinutes;
         this.kernel = kernel;
     }
 
@@ -438,7 +448,7 @@
             }
 
 
-         }
+        }
         //
         // admin objects (think message queuse and topics)
         //
@@ -451,35 +461,35 @@
         }
         // add configured admin objects
         for (int i = 0; i < geronimoConnector.getAdminobjectArray().length; i++) {
-             GerAdminobjectType gerAdminObject = geronimoConnector.getAdminobjectArray()[i];
+            GerAdminobjectType gerAdminObject = geronimoConnector.getAdminobjectArray()[i];
+
+            String adminObjectInterface = gerAdminObject.getAdminobjectInterface().getStringValue();
+            AdminobjectType adminObject = (AdminobjectType) adminObjectInterfaceMap.get(adminObjectInterface);
+            if (adminObject == null) {
+                throw new DeploymentException("No admin object declared for interface: " + adminObjectInterface);
+            }
+
+            for (int j = 0; j < gerAdminObject.getAdminobjectInstanceArray().length; j++) {
+                GerAdminobjectInstanceType gerAdminObjectInstance = gerAdminObject.getAdminobjectInstanceArray()[j];
+
+                // create the adminObjectGBean
+                GBeanInfoBuilder adminObjectInfoFactory = new GBeanInfoBuilder("org.apache.geronimo.connector.AdminObjectWrapper", cl);
+                ConfigProperty[] configProperties = getConfigProperties(adminObject.getConfigPropertyArray(), gerAdminObjectInstance.getConfigPropertySettingArray());
+                GBeanMBean adminObjectGBean = setUpDynamicGBean(adminObjectInfoFactory, configProperties, cl);
 
-             String adminObjectInterface = gerAdminObject.getAdminobjectInterface().getStringValue();
-             AdminobjectType adminObject = (AdminobjectType) adminObjectInterfaceMap.get(adminObjectInterface);
-             if (adminObject == null) {
-                 throw new DeploymentException("No admin object declared for interface: " + adminObjectInterface);
-             }
-
-             for (int j = 0; j < gerAdminObject.getAdminobjectInstanceArray().length; j++) {
-                 GerAdminobjectInstanceType gerAdminObjectInstance = gerAdminObject.getAdminobjectInstanceArray()[j];
-
-                 // create the adminObjectGBean
-                 GBeanInfoBuilder adminObjectInfoFactory = new GBeanInfoBuilder("org.apache.geronimo.connector.AdminObjectWrapper", cl);
-                 ConfigProperty[] configProperties = getConfigProperties(adminObject.getConfigPropertyArray(), gerAdminObjectInstance.getConfigPropertySettingArray());
-                 GBeanMBean adminObjectGBean = setUpDynamicGBean(adminObjectInfoFactory, configProperties, cl);
-
-                 // set the standard properties
-                 try {
-                     adminObjectGBean.setAttribute("adminObjectInterface", cl.loadClass(adminObjectInterface));
-                     adminObjectGBean.setAttribute("adminObjectClass", cl.loadClass(adminObject.getAdminobjectClass().getStringValue()));
-                 } catch (Exception e) {
-                     throw new DeploymentException("Could not initialize AdminObject", e);
-                 }
-
-                 // add it
-                 ObjectName adminObjectObjectName = NameFactory.getResourceComponentName(null, null, null, null, gerAdminObjectInstance.getMessageDestinationName(), NameFactory.JCA_ADMIN_OBJECT, moduleJ2eeContext);
-                 earContext.addGBean(adminObjectObjectName, adminObjectGBean);
-             }
-         }
+                // set the standard properties
+                try {
+                    adminObjectGBean.setAttribute("adminObjectInterface", cl.loadClass(adminObjectInterface));
+                    adminObjectGBean.setAttribute("adminObjectClass", cl.loadClass(adminObject.getAdminobjectClass().getStringValue()));
+                } catch (Exception e) {
+                    throw new DeploymentException("Could not initialize AdminObject", e);
+                }
+
+                // add it
+                ObjectName adminObjectObjectName = NameFactory.getResourceComponentName(null, null, null, null, gerAdminObjectInstance.getMessageDestinationName(), NameFactory.JCA_ADMIN_OBJECT, moduleJ2eeContext);
+                earContext.addGBean(adminObjectObjectName, adminObjectGBean);
+            }
+        }
     }
 
     private Map getActivationSpecInfoMap(MessagelistenerType[] messagelistenerArray, ClassLoader cl) throws DeploymentException {
@@ -512,9 +522,9 @@
             }
 
             GBeanInfo gbeanInfo = infoFactory.getBeanInfo();
-            Class activationSpecClass = null;
             try {
-                activationSpecClass = cl.loadClass(activationSpecClassName);
+                //make sure the class is available, but we don't use it.
+                cl.loadClass(activationSpecClassName);
             } catch (ClassNotFoundException e) {
                 throw new DeploymentException("Could not load ActivationSpec class", e);
             }
@@ -659,24 +669,29 @@
             throw new DeploymentException("Unexpected transaction support element");
         }
         PoolingSupport pooling = null;
-        //TODO configure this
-//        int idleTimeoutMinutes = 15;
         if (connectionManager.getSinglePool() != null) {
-            pooling = new SinglePool(connectionManager.getSinglePool().getMaxSize(),
-                    connectionManager.getSinglePool().getBlockingTimeoutMilliseconds(),
-//                    idleTimeoutMinutes,
-                    connectionManager.getSinglePool().getMatchOne() != null,
-                    connectionManager.getSinglePool().getMatchAll() != null,
-                    connectionManager.getSinglePool().getSelectOneAssumeMatch() != null);
+            GerSinglepoolType pool = connectionManager.getSinglePool();
+
+            pooling = new SinglePool(
+                    pool.isSetMaxSize() ? pool.getMaxSize() : defaultMaxSize,
+                    pool.isSetMinSize() ? pool.getMinSize() : defaultMinSize,
+                    pool.isSetBlockingTimeoutMilliseconds() ? pool.getBlockingTimeoutMilliseconds() : defaultBlockingTimeoutMilliseconds,
+                    pool.isSetIdleTimeoutMinutes() ? pool.getIdleTimeoutMinutes() : defaultIdleTimeoutMinutes,
+                    pool.getMatchOne() != null,
+                    pool.getMatchAll() != null,
+                    pool.getSelectOneAssumeMatch() != null);
         } else if (connectionManager.getPartitionedPool() != null) {
-            pooling = new PartitionedPool(connectionManager.getPartitionedPool().getPartitionByConnectionrequestinfo() != null,
-                    connectionManager.getPartitionedPool().getPartitionBySubject() != null,
-                    connectionManager.getPartitionedPool().getMaxSize(),
-                    connectionManager.getPartitionedPool().getBlockingTimeoutMilliseconds(),
-//                    idleTimeoutMinutes,
-                    connectionManager.getPartitionedPool().getMatchOne() != null,
-                    connectionManager.getPartitionedPool().getMatchAll() != null,
-                    connectionManager.getPartitionedPool().getSelectOneAssumeMatch() != null);
+            GerPartitionedpoolType pool = connectionManager.getPartitionedPool();
+            pooling = new PartitionedPool(
+                    pool.isSetMaxSize() ? pool.getMaxSize() : defaultMaxSize,
+                    pool.isSetMinSize() ? pool.getMinSize() : defaultMinSize,
+                    pool.isSetBlockingTimeoutMilliseconds() ? pool.getBlockingTimeoutMilliseconds() : defaultBlockingTimeoutMilliseconds,
+                    pool.isSetIdleTimeoutMinutes() ? pool.getIdleTimeoutMinutes() : defaultIdleTimeoutMinutes,
+                    pool.getMatchOne() != null,
+                    pool.getMatchAll() != null,
+                    pool.getSelectOneAssumeMatch() != null,
+                    pool.isSetPartitionByConnectionrequestinfo(),
+                    pool.isSetPartitionBySubject());
         } else if (connectionManager.getNoPool() != null) {
             pooling = new NoPool();
         } else {
@@ -836,12 +851,19 @@
     public static final GBeanInfo GBEAN_INFO;
 
     static {
-        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(ConnectorModuleBuilder.class);
-        infoFactory.addAttribute("kernel", Kernel.class, false);
-        infoFactory.addInterface(ModuleBuilder.class);
-        infoFactory.addInterface(ResourceReferenceBuilder.class);
-        infoFactory.setConstructor(new String[] {"kernel"});
-        GBEAN_INFO = infoFactory.getBeanInfo();
+        GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder(ConnectorModuleBuilder.class);
+        infoBuilder.addAttribute("defaultMaxSize", int.class, true);
+        infoBuilder.addAttribute("defaultMinSize", int.class, true);
+        infoBuilder.addAttribute("defaultBlockingTimeoutMilliseconds", int.class, true);
+        infoBuilder.addAttribute("defaultIdleTimeoutMinutes", int.class, true);
+
+        infoBuilder.addAttribute("kernel", Kernel.class, false);
+
+        infoBuilder.addInterface(ModuleBuilder.class);
+        infoBuilder.addInterface(ResourceReferenceBuilder.class);
+
+        infoBuilder.setConstructor(new String[]{"defaultMaxSize", "defaultMinSize", "defaultBlockingTimeoutMilliseconds", "defaultIdleTimeoutMinutes", "kernel"});
+        GBEAN_INFO = infoBuilder.getBeanInfo();
     }
 
     public static GBeanInfo getGBeanInfo() {

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java	Mon Nov  1 09:34:13 2004
@@ -26,6 +26,7 @@
 import org.apache.geronimo.gbean.GBeanInfo;
 import org.apache.geronimo.gbean.GBeanInfoBuilder;
 import org.apache.geronimo.transaction.manager.NamedXAResource;
+import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport;
 
 /**
  * @version $Rev$ $Date$
@@ -98,6 +99,18 @@
         return getPooling().getPartitionMaxSize();
     }
 
+    public void setPartitionMaxSize(int maxSize) throws InterruptedException {
+        getPooling().setPartitionMaxSize(maxSize);
+    }
+
+    public int getPartitionMinSize() {
+        return getPooling().getPartitionMinSize();
+    }
+
+    public void setPartitionMinSize(int minSize) {
+        getPooling().setPartitionMinSize(minSize);
+    }
+
     public int getIdleConnectionCount() {
         return getPooling().getIdleConnectionCount();
     }
@@ -106,6 +119,21 @@
         return getPooling().getConnectionCount();
     }
 
+    public int getBlockingTimeoutMilliseconds() {
+        return getPooling().getBlockingTimeoutMilliseconds();
+    }
+
+    public void setBlockingTimeoutMilliseconds(int timeoutMilliseconds) {
+        getPooling().setBlockingTimeoutMilliseconds(timeoutMilliseconds);
+    }
+
+    public int getIdleTimeoutMinutes() {
+        return getPooling().getIdleTimeoutMinutes();
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+        getPooling().setIdleTimeoutMinutes(idleTimeoutMinutes);
+    }
 
     private ConnectionInterceptor getStack() {
         return interceptors.getStack();
@@ -115,26 +143,30 @@
         return interceptors.getRecoveryStack();
     }
 
-    private PoolingAttributes getPooling() {
+    //public for persistence of pooling attributes (max, min size, blocking/idle timeouts)
+    public PoolingSupport getPooling() {
         return interceptors.getPoolingAttributes();
     }
 
     public interface Interceptors {
         ConnectionInterceptor getStack();
+
         ConnectionInterceptor getRecoveryStack();
-        PoolingAttributes getPoolingAttributes();
+
+        PoolingSupport getPoolingAttributes();
     }
 
     protected static final GBeanInfo GBEAN_INFO;
 
 
     static {
-        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(AbstractConnectionManager.class);
+        GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder(AbstractConnectionManager.class);
 
-        infoFactory.addInterface(ConnectionManagerFactory.class);
-        infoFactory.addInterface(PoolingAttributes.class);
+        infoBuilder.addInterface(ConnectionManagerFactory.class);
+        //these attributes are persisted via the pooling state.
+        infoBuilder.addInterface(PoolingAttributes.class);
 
-        GBEAN_INFO = infoFactory.getBeanInfo();
+        GBEAN_INFO = infoBuilder.getBeanInfo();
     }
 
 }

Added: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractSinglePoolConnectionInterceptor.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/AbstractSinglePoolConnectionInterceptor.java	Mon Nov  1 09:34:13 2004
@@ -0,0 +1,281 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.connector.outbound;
+
+import java.util.TimerTask;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Timer;
+import javax.resource.spi.ManagedConnectionFactory;
+import javax.resource.spi.ConnectionRequestInfo;
+import javax.resource.ResourceException;
+import javax.security.auth.Subject;
+
+import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
+import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @version $Rev:  $ $Date:  $
+ */
+public abstract class AbstractSinglePoolConnectionInterceptor implements ConnectionInterceptor, PoolingAttributes {
+    protected static Log log = LogFactory.getLog(SinglePoolConnectionInterceptor.class.getName());
+    protected final ConnectionInterceptor next;
+    private final ReadWriteLock resizeLock = new WriterPreferenceReadWriteLock();
+    protected FIFOSemaphore permits;
+    protected int blockingTimeoutMilliseconds;
+    protected int connectionCount = 0;
+    private long idleTimeoutMilliseconds;
+    private IdleReleaser idleReleaser;
+    protected Timer timer = PoolIdleReleaserTimer.getTimer();
+    protected int minSize = 0;
+    protected int shrinkLater = 0;
+
+    public AbstractSinglePoolConnectionInterceptor(final ConnectionInterceptor next,
+                                                   int maxSize,
+                                                   int minSize,
+                                                   int blockingTimeoutMilliseconds,
+                                                   int idleTimeoutMinutes) {
+        this.next = next;
+        this.minSize = minSize;
+        this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
+        setIdleTimeoutMinutes(idleTimeoutMinutes);
+        permits = new FIFOSemaphore(maxSize);
+    }
+
+    public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
+        if (connectionInfo.getManagedConnectionInfo().getManagedConnection() != null) {
+            return;
+        }
+        try {
+            resizeLock.readLock().acquire();
+            try {
+                if (permits.attempt(blockingTimeoutMilliseconds)) {
+                    internalGetConnection(connectionInfo);
+                } else {
+                    throw new ResourceException("No ManagedConnections available "
+                            + "within configured blocking timeout ( "
+                            + blockingTimeoutMilliseconds
+                            + " [ms] )");
+
+                }
+            } finally {
+                resizeLock.readLock().release();
+            }
+
+        } catch (InterruptedException ie) {
+            throw new ResourceException("Interrupted while requesting permit!");
+        } // end of try-catch
+    }
+
+    protected abstract void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException;
+
+    public void returnConnection(ConnectionInfo connectionInfo,
+                                 ConnectionReturnAction connectionReturnAction) {
+        if (log.isTraceEnabled()) {
+            log.trace("returning connection" + connectionInfo.getConnectionHandle());
+        }
+        try {
+            resizeLock.readLock().acquire();
+        } catch (InterruptedException e) {
+            //TODO figure out something better to do here!!!
+            throw new RuntimeException("Interrupted before returning connection! Pool is now in an invalid state!");
+        }
+        try {
+            ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
+            if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE && mci.hasConnectionHandles()) {
+                return;
+            }
+
+            boolean wasInPool = internalReturn(connectionInfo, connectionReturnAction);
+
+            if (!wasInPool) {
+                permits.release();
+            }
+        } finally {
+            resizeLock.readLock().release();
+        }
+    }
+
+    protected abstract boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction);
+
+    public int getPartitionCount() {
+        return 1;
+    }
+
+    public abstract int getPartitionMaxSize();
+
+    public void setPartitionMaxSize(int maxSize) throws InterruptedException {
+        if (maxSize <= 0) {
+            throw new IllegalArgumentException("Max size must be positive, not " + maxSize);
+        }
+        if (maxSize != getPartitionMaxSize()) {
+            resizeLock.writeLock().acquire();
+            try {
+                //example: old maxsize 40, permits 20, connection count 20
+                //new maxSize 10
+                //shrinkLater is 10
+                //shrinkNow is 20
+                //2nd example: old maxsize 30, permits 10, connection count 10
+                //new maxSize 40
+                //shrinkLater and shrinkNow are 0.
+                int checkedOut = (int) permits.permits();
+                shrinkLater = checkedOut - maxSize;
+                if (shrinkLater < 0) {
+                    shrinkLater = 0;
+                }
+                int shrinkNow = checkedOut + connectionCount - maxSize - shrinkLater;
+                if (shrinkNow < 0) {
+                    shrinkNow = 0;
+                }
+
+                permits = new FIFOSemaphore(maxSize);
+                //1st example: acquire 10 (all)
+                //2nd example: acquire 10 (same as in old semaphore)
+                for (int i = 0; i < checkedOut - shrinkLater; i++) {
+                    permits.acquire();
+                }
+                //1st example: copy 0 (none)
+                //2nd example: copy 10 (all)
+                transferConnections(maxSize, shrinkNow);
+            } finally {
+                resizeLock.writeLock().release();
+            }
+        }
+    }
+
+    protected abstract void transferConnections(int maxSize, int shrinkNow);
+
+    public abstract int getIdleConnectionCount();
+
+    public int getConnectionCount() {
+        return connectionCount;
+    }
+
+    public int getPartitionMinSize() {
+        return minSize;
+    }
+
+    public void setPartitionMinSize(int minSize) {
+        this.minSize = minSize;
+    }
+
+    public int getBlockingTimeoutMilliseconds() {
+        return blockingTimeoutMilliseconds;
+    }
+
+    public void setBlockingTimeoutMilliseconds(int blockingTimeoutMilliseconds) {
+        if (blockingTimeoutMilliseconds < 0) {
+            throw new IllegalArgumentException("blockingTimeoutMilliseconds must be positive or 0, not " + blockingTimeoutMilliseconds);
+        }
+        if (blockingTimeoutMilliseconds == 0) {
+            this.blockingTimeoutMilliseconds = Integer.MAX_VALUE;
+        } else {
+            this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
+        }
+    }
+
+    public int getIdleTimeoutMinutes() {
+        return (int) idleTimeoutMilliseconds / (1000 * 60);
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+        if (idleTimeoutMinutes < 0) {
+            throw new IllegalArgumentException("idleTimeoutMinutes must be positive or 0, not " + idleTimeoutMinutes);
+        }
+        if (idleReleaser != null) {
+            idleReleaser.cancel();
+        }
+        if (idleTimeoutMinutes > 0) {
+            this.idleTimeoutMilliseconds = idleTimeoutMinutes * 60 * 1000;
+            idleReleaser = new IdleReleaser();
+            timer.schedule(idleReleaser, this.idleTimeoutMilliseconds, this.idleTimeoutMilliseconds);
+        }
+    }
+
+    protected abstract void getExpiredManagedConnectionInfos(long threshold, ArrayList killList);
+
+    protected abstract boolean addToPool(ManagedConnectionInfo mci);
+
+    private class IdleReleaser extends TimerTask {
+
+        public void run() {
+            try {
+                resizeLock.readLock().acquire();
+            } catch (InterruptedException e) {
+                return;
+            }
+            try {
+                long threshold = System.currentTimeMillis() - idleTimeoutMilliseconds;
+                ArrayList killList = new ArrayList(getPartitionMaxSize());
+                getExpiredManagedConnectionInfos(threshold, killList);
+                for (Iterator i = killList.iterator(); i.hasNext();) {
+                    ManagedConnectionInfo managedConnectionInfo = (ManagedConnectionInfo) i.next();
+                    ConnectionInfo killInfo = new ConnectionInfo(managedConnectionInfo);
+                    internalReturn(killInfo, ConnectionReturnAction.DESTROY);
+                }
+                permits.release(killList.size());
+            } finally {
+                resizeLock.readLock().release();
+            }
+        }
+
+    }
+
+    protected class FillTask extends TimerTask {
+        private final ManagedConnectionFactory managedConnectionFactory;
+        private final Subject subject;
+        private final ConnectionRequestInfo cri;
+
+        public FillTask(ConnectionInfo connectionInfo) {
+            managedConnectionFactory = connectionInfo.getManagedConnectionInfo().getManagedConnectionFactory();
+            subject = connectionInfo.getManagedConnectionInfo().getSubject();
+            cri = connectionInfo.getManagedConnectionInfo().getConnectionRequestInfo();
+        }
+
+        public void run() {
+            try {
+                resizeLock.readLock().acquire();
+            } catch (InterruptedException e) {
+                return;
+            }
+            try {
+                while (connectionCount < minSize) {
+                    ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, cri);
+                    mci.setSubject(subject);
+                    ConnectionInfo ci = new ConnectionInfo(mci);
+                    try {
+                        next.getConnection(ci);
+                    } catch (ResourceException e) {
+                        return;
+                    }
+                    boolean added = false;
+                    added = addToPool(mci);
+                    if (!added) {
+                        internalReturn(ci, ConnectionReturnAction.DESTROY);
+                        return;
+                    }
+                }
+            } finally {
+                resizeLock.readLock().release();
+            }
+        }
+
+    }
+}

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java	Mon Nov  1 09:34:13 2004
@@ -52,7 +52,7 @@
 
         private final ConnectionInterceptor stack;
         private final ConnectionInterceptor recoveryStack;
-        private final PoolingAttributes poolingSupport;
+        private final PoolingSupport poolingSupport;
 
         /**
          * Order of constructed interceptors:
@@ -110,7 +110,7 @@
             return recoveryStack;
         }
 
-        public PoolingAttributes getPoolingAttributes() {
+        public PoolingSupport getPoolingAttributes() {
             return poolingSupport;
         }
     }
@@ -119,19 +119,19 @@
     public static final GBeanInfo GBEAN_INFO;
 
     static {
-        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(GenericConnectionManager.class, AbstractConnectionManager.GBEAN_INFO);
+        GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder(GenericConnectionManager.class, AbstractConnectionManager.GBEAN_INFO);
 
-        infoFactory.addAttribute("name", String.class, true);
-        infoFactory.addAttribute("transactionSupport", TransactionSupport.class, true);
-        infoFactory.addAttribute("pooling", PoolingSupport.class, true);
+        infoBuilder.addAttribute("name", String.class, true);
+        infoBuilder.addAttribute("transactionSupport", TransactionSupport.class, true);
+        infoBuilder.addAttribute("pooling", PoolingSupport.class, true);
 
-        infoFactory.addAttribute("objectName", String.class, false);
+        infoBuilder.addAttribute("objectName", String.class, false);
 
-        infoFactory.addReference("ConnectionTracker", ConnectionTracker.class);
-        infoFactory.addReference("RealmBridge", RealmBridge.class);
-        infoFactory.addReference("TransactionContextManager", TransactionContextManager.class);
+        infoBuilder.addReference("ConnectionTracker", ConnectionTracker.class);
+        infoBuilder.addReference("RealmBridge", RealmBridge.class);
+        infoBuilder.addReference("TransactionContextManager", TransactionContextManager.class);
 
-        infoFactory.setConstructor(new String[]{
+        infoBuilder.setConstructor(new String[]{
             "transactionSupport",
             "pooling",
             "objectName",
@@ -139,7 +139,7 @@
             "ConnectionTracker",
             "TransactionContextManager"});
 
-        GBEAN_INFO = infoFactory.getBeanInfo();
+        GBEAN_INFO = infoBuilder.getBeanInfo();
     }
 
     public static GBeanInfo getGBeanInfo() {

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/MultiPoolConnectionInterceptor.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/MultiPoolConnectionInterceptor.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/MultiPoolConnectionInterceptor.java	Mon Nov  1 09:34:13 2004
@@ -94,6 +94,26 @@
         return singlePoolFactory.getPartitionMaxSize();
     }
 
+    public void setPartitionMaxSize(int maxSize) throws InterruptedException {
+        singlePoolFactory.setPartitionMaxSize(maxSize);
+        for (Iterator iterator = pools.entrySet().iterator(); iterator.hasNext();) {
+            PoolingAttributes poolingAttributes = (PoolingAttributes) ((Map.Entry) iterator.next()).getValue();
+            poolingAttributes.setPartitionMaxSize(maxSize);
+        }
+    }
+
+    public int getPartitionMinSize() {
+        return singlePoolFactory.getPartitionMinSize();
+    }
+
+    public void setPartitionMinSize(int minSize) {
+        singlePoolFactory.setPartitionMinSize(minSize);
+        for (Iterator iterator = pools.entrySet().iterator(); iterator.hasNext();) {
+            PoolingAttributes poolingAttributes = (PoolingAttributes) ((Map.Entry) iterator.next()).getValue();
+            poolingAttributes.setPartitionMinSize(minSize);
+        }
+    }
+
     public int getIdleConnectionCount() {
         int count = 0;
         for (Iterator iterator = pools.entrySet().iterator(); iterator.hasNext();) {
@@ -110,6 +130,30 @@
             count += poolingAttributes.getConnectionCount();
         }
         return count;
+    }
+
+    public int getBlockingTimeoutMilliseconds() {
+        return singlePoolFactory.getBlockingTimeoutMilliseconds();
+    }
+
+    public void setBlockingTimeoutMilliseconds(int timeoutMilliseconds) {
+        singlePoolFactory.setBlockingTimeoutMilliseconds(timeoutMilliseconds);
+        for (Iterator iterator = pools.entrySet().iterator(); iterator.hasNext();) {
+            PoolingAttributes poolingAttributes = (PoolingAttributes) ((Map.Entry) iterator.next()).getValue();
+            poolingAttributes.setBlockingTimeoutMilliseconds(timeoutMilliseconds);
+        }
+    }
+
+    public int getIdleTimeoutMinutes() {
+        return singlePoolFactory.getIdleTimeoutMinutes();
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+        singlePoolFactory.setIdleTimeoutMinutes(idleTimeoutMinutes);
+        for (Iterator iterator = pools.entrySet().iterator(); iterator.hasNext();) {
+            PoolingAttributes poolingAttributes = (PoolingAttributes) ((Map.Entry) iterator.next()).getValue();
+            poolingAttributes.setIdleTimeoutMinutes(idleTimeoutMinutes);
+        }
     }
 
     static class SubjectCRIKey {

Added: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolIdleReleaserTimer.java
==============================================================================
--- (empty file)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolIdleReleaserTimer.java	Mon Nov  1 09:34:13 2004
@@ -0,0 +1,31 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.connector.outbound;
+
+import java.util.Timer;
+
+/**
+ * @version $Rev:  $ $Date:  $
+ */
+public class PoolIdleReleaserTimer {
+
+    private static final Timer timer = new Timer(true);
+
+    public static Timer getTimer() {
+        return timer;
+    }
+}

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolingAttributes.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolingAttributes.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/PoolingAttributes.java	Mon Nov  1 09:34:13 2004
@@ -23,9 +23,23 @@
 public interface PoolingAttributes {
     int getPartitionCount();
 
-    int getPartitionMaxSize();
+    int getConnectionCount();
 
     int getIdleConnectionCount();
 
-    int getConnectionCount();
+    int getPartitionMaxSize();
+
+    void setPartitionMaxSize(int maxSize) throws InterruptedException;
+
+    int getPartitionMinSize();
+
+    void setPartitionMinSize(int minSize);
+
+    int getBlockingTimeoutMilliseconds();
+
+    void setBlockingTimeoutMilliseconds(int timeoutMilliseconds);
+
+    int getIdleTimeoutMinutes();
+
+    void setIdleTimeoutMinutes(int idleTimeoutMinutes);
 }

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolConnectionInterceptor.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolConnectionInterceptor.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolConnectionInterceptor.java	Mon Nov  1 09:34:13 2004
@@ -17,15 +17,11 @@
 
 package org.apache.geronimo.connector.outbound;
 
+import java.util.ArrayList;
 import java.util.Collections;
-
 import javax.resource.ResourceException;
 import javax.resource.spi.ManagedConnection;
 
-import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 /**
  * SinglePoolConnectionInterceptor chooses a single connection from the pool.  If selectOneAssumeMatch
  * is true, it simply returns the selected connection.
@@ -36,184 +32,175 @@
  * selected connection does match before returning it: if not it throws an exception.
  *
  * @version $Rev$ $Date$
- *
  */
-public class SinglePoolConnectionInterceptor implements ConnectionInterceptor, PoolingAttributes {
-
-    private static Log log = LogFactory.getLog(SinglePoolConnectionInterceptor.class.getName());
+public class SinglePoolConnectionInterceptor extends AbstractSinglePoolConnectionInterceptor {
 
 
-    private final ConnectionInterceptor next;
-
-    private FIFOSemaphore permits;
+    private boolean selectOneAssumeMatch;
 
     private PoolDeque pool;
 
-    private int blockingTimeout;
-    private boolean selectOneAssumeMatch;
 
-    private int connectionCount = 0;
-
-    public SinglePoolConnectionInterceptor(
-            final ConnectionInterceptor next,
-            int maxSize,
-            int blockingTimeout,
-            boolean selectOneAssumeMatch) {
-        this.next = next;
-        this.blockingTimeout = blockingTimeout;
-        permits = new FIFOSemaphore(maxSize);
+    public SinglePoolConnectionInterceptor(final ConnectionInterceptor next,
+                                           int maxSize,
+                                           int minSize,
+                                           int blockingTimeoutMilliseconds,
+                                           int idleTimeoutMinutes,
+                                           boolean selectOneAssumeMatch) {
+        super(next, maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes);
         pool = new PoolDeque(maxSize);
         this.selectOneAssumeMatch = selectOneAssumeMatch;
     }
 
-    public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
-        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
-        if (mci.getManagedConnection() != null) {
-            return;
-        }
-        try {
-            if (permits.attempt(blockingTimeout)) {
-                ManagedConnectionInfo newMCI = null;
-                synchronized (pool) {
-                    if (pool.isEmpty()) {
-                        next.getConnection(connectionInfo);
-                        connectionCount++;
-                        if (log.isTraceEnabled()) {
-                            log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo());
-                        }
-                        return;
-                    } else {
-                        newMCI = pool.removeLast();
-                    }
-                    if (selectOneAssumeMatch) {
-                        connectionInfo.setManagedConnectionInfo(newMCI);
-                        if (log.isTraceEnabled()) {
-                            log.trace("Returning pooled connection without checking matching " + connectionInfo.getManagedConnectionInfo());
-                        }
-                        return;
-                    }
-                    try {
-                        ManagedConnection matchedMC =
-//                                newMCI.getManagedConnection();
-                                newMCI
-                                .getManagedConnectionFactory()
-                                .matchManagedConnections(
-                                        Collections.singleton(
-                                                newMCI.getManagedConnection()),
-                                        mci.getSubject(),
-                                        mci.getConnectionRequestInfo());
-                        if (matchedMC != null) {
-                            connectionInfo.setManagedConnectionInfo(newMCI);
-                            if (log.isTraceEnabled()) {
-                                log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo());
-                            }
-                            return;
-                        } else {
-                            //matching failed.
-                            ConnectionInfo returnCI = new ConnectionInfo();
-                            returnCI.setManagedConnectionInfo(newMCI);
-                            returnConnection(
-                                    returnCI,
-                                    ConnectionReturnAction.RETURN_HANDLE);
-                            throw new ResourceException("The pooling strategy does not match the MatchManagedConnections implementation.  Please investigate and reconfigure this pool");
-                        }
-                    } catch (ResourceException e) {
-                        //something is wrong: destroy connection, rethrow, release permit
-                        ConnectionInfo returnCI = new ConnectionInfo();
-                        returnCI.setManagedConnectionInfo(newMCI);
-                        returnConnection(
-                                returnCI,
-                                ConnectionReturnAction.DESTROY);
-                        throw e;
-                    } // end of try-catch
+    protected void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException {
+        synchronized (pool) {
+            ManagedConnectionInfo newMCI = null;
+            if (pool.isEmpty()) {
+                next.getConnection(connectionInfo);
+                connectionCount++;
+                if (log.isTraceEnabled()) {
+                    log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo());
                 }
+                return;
             } else {
-                throw new ResourceException(
-                        "No ManagedConnections available "
-                        + "within configured blocking timeout ( "
-                        + blockingTimeout
-                        + " [ms] )");
-
-            } // end of else
-
-        } catch (InterruptedException ie) {
-            throw new ResourceException("Interrupted while requesting permit!");
-        } // end of try-catch
-    }
-
-    public void returnConnection(
-            ConnectionInfo connectionInfo,
-            ConnectionReturnAction connectionReturnAction) {
-        if (log.isTraceEnabled()) {
-            log.trace("returning connection" + connectionInfo.getConnectionHandle());
-        }
-        boolean wasInPool = false;
-        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
-        if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
-            synchronized (pool) {
-                wasInPool = pool.remove(mci);
+                newMCI = pool.removeLast();
+            }
+            if (connectionCount < minSize) {
+                timer.schedule(new FillTask(connectionInfo), 10);
             }
-        } else {
-            if (mci.hasConnectionHandles()) {
+            if (selectOneAssumeMatch) {
+                connectionInfo.setManagedConnectionInfo(newMCI);
+                if (log.isTraceEnabled()) {
+                    log.trace("Returning pooled connection without checking matching " + connectionInfo.getManagedConnectionInfo());
+                }
                 return;
             }
-        } // end of else
+            try {
+                ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
+                ManagedConnection matchedMC =
+                        newMCI
+                        .getManagedConnectionFactory()
+                        .matchManagedConnections(Collections.singleton(newMCI.getManagedConnection()),
+                                mci.getSubject(),
+                                mci.getConnectionRequestInfo());
+                if (matchedMC != null) {
+                    connectionInfo.setManagedConnectionInfo(newMCI);
+                    if (log.isTraceEnabled()) {
+                        log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo());
+                    }
+                    return;
+                } else {
+                    //matching failed.
+                    ConnectionInfo returnCI = new ConnectionInfo();
+                    returnCI.setManagedConnectionInfo(newMCI);
+                    returnConnection(returnCI,
+                            ConnectionReturnAction.RETURN_HANDLE);
+                    throw new ResourceException("The pooling strategy does not match the MatchManagedConnections implementation.  Please investigate and reconfigure this pool");
+                }
+            } catch (ResourceException e) {
+                //something is wrong: destroy connection, rethrow, release permit
+                ConnectionInfo returnCI = new ConnectionInfo();
+                returnCI.setManagedConnectionInfo(newMCI);
+                returnConnection(returnCI,
+                        ConnectionReturnAction.DESTROY);
+                throw e;
+            }
+        }
+    }
 
+    protected boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
+        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
         ManagedConnection mc = mci.getManagedConnection();
         try {
             mc.cleanup();
         } catch (ResourceException e) {
             connectionReturnAction = ConnectionReturnAction.DESTROY;
         }
-
-        if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
-            next.returnConnection(connectionInfo, connectionReturnAction);
-            connectionCount--;
-        } else {
-            synchronized (pool) {
+        boolean wasInPool = false;
+        synchronized (pool) {
+            if (shrinkLater > 0) {
+                //nothing can get in the pool while shrinkLater > 0, so wasInPool is false here.
+                connectionReturnAction = ConnectionReturnAction.DESTROY;
+                shrinkLater--;
+            } else if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE) {
                 mci.setLastUsed(System.currentTimeMillis());
-                pool.addLast(mci);
+                pool.add(mci);
+                return wasInPool;
+            } else {
+                wasInPool = pool.remove(mci);
             }
-
-        } // end of else
-
-        if (!wasInPool) {
-            permits.release();
         }
-    }
-
-    public int getPartitionCount() {
-        return 1;
+        //we must destroy connection.
+        next.returnConnection(connectionInfo, connectionReturnAction);
+        connectionCount--;
+        return wasInPool;
     }
 
     public int getPartitionMaxSize() {
         return pool.capacity();
     }
 
+    protected void transferConnections(int maxSize, int shrinkNow) {
+        //1st example: copy 0 (none)
+        //2nd example: copy 10 (all)
+        PoolDeque oldPool = pool;
+        pool = new PoolDeque(maxSize);
+        //since we have replaced pool already, pool.remove will be very fast:-)
+        for (int i = 0; i < shrinkNow; i++) {
+            ConnectionInfo killInfo = new ConnectionInfo(oldPool.peek(i));
+            internalReturn(killInfo, ConnectionReturnAction.DESTROY);
+        }
+        for (int i = shrinkNow; i < connectionCount; i++) {
+            pool.add(oldPool.peek(i));
+        }
+    }
+
     public int getIdleConnectionCount() {
         return pool.currentSize();
     }
 
-    public int getConnectionCount() {
-        return connectionCount;
+
+    protected void getExpiredManagedConnectionInfos(long threshold, ArrayList killList) {
+        synchronized (pool) {
+            for (int i = 0; i < pool.currentSize(); i++) {
+                ManagedConnectionInfo mci = pool.peek(i);
+                if (mci.getLastUsed() < threshold) {
+                    killList.add(mci);
+                }
+            }
+        }
     }
 
+    protected boolean addToPool(ManagedConnectionInfo mci) {
+        boolean added;
+        synchronized (pool) {
+            connectionCount++;
+            added = getPartitionMaxSize() > getIdleConnectionCount();
+            if (added) {
+                pool.add(mci);
+            }
+        }
+        return added;
+    }
+
+
     static class PoolDeque {
 
         private final ManagedConnectionInfo[] deque;
-        private int first = 0;
+        private final int first = 0;
         private int last = -1;
 
         public PoolDeque(int size) {
             deque = new ManagedConnectionInfo[size];
         }
 
+        //internal
         public boolean isEmpty() {
             return first > last;
         }
 
-
-        public void addLast(ManagedConnectionInfo mci) {
+        //internal
+        public void add(ManagedConnectionInfo mci) {
             if (last == deque.length - 1) {
                 throw new IllegalStateException("deque is full");
             }
@@ -221,14 +208,15 @@
             deque[++last] = mci;
         }
 
-        public ManagedConnectionInfo peekLast() {
-            if (isEmpty()) {
-                throw new IllegalStateException("deque is empty");
+        //internal
+        public ManagedConnectionInfo peek(int i) {
+            if (i < first || i > last) {
+                throw new IllegalStateException("index is out of current range");
             }
-
-            return deque[last];
+            return deque[i];
         }
 
+        //internal
         public ManagedConnectionInfo removeLast() {
             if (isEmpty()) {
                 throw new IllegalStateException("deque is empty");
@@ -237,6 +225,7 @@
             return deque[last--];
         }
 
+        //internal
         public boolean remove(ManagedConnectionInfo mci) {
             for (int i = first; i <= last; i++) {
                 if (deque[i] == mci) {
@@ -251,13 +240,15 @@
             return false;
         }
 
+        //internal
         public int capacity() {
             return deque.length;
         }
 
+        //internal
         public int currentSize() {
             return last - first + 1;
         }
     }
 
-} // SinglePoolConnectionInterceptor
+}

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolMatchAllConnectionInterceptor.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolMatchAllConnectionInterceptor.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/SinglePoolMatchAllConnectionInterceptor.java	Mon Nov  1 09:34:13 2004
@@ -17,18 +17,14 @@
 
 package org.apache.geronimo.connector.outbound;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
-
 import javax.resource.ResourceException;
 import javax.resource.spi.ManagedConnection;
 import javax.resource.spi.ManagedConnectionFactory;
 
-import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 /**
  * This pool is the most spec-compliant pool.  It can be used by itself with no partitioning.
  * It is apt to be the slowest pool.
@@ -38,112 +34,136 @@
  *
  * @version $Rev$ $Date$
  */
-public class SinglePoolMatchAllConnectionInterceptor implements ConnectionInterceptor, PoolingAttributes {
+public class SinglePoolMatchAllConnectionInterceptor extends AbstractSinglePoolConnectionInterceptor {
+//        implements ConnectionInterceptor, PoolingAttributes {
+
+//    private static Log log = LogFactory.getLog(SinglePoolMatchAllConnectionInterceptor.class.getName());
 
-    private static Log log = LogFactory.getLog(SinglePoolMatchAllConnectionInterceptor.class.getName());
 
+//    private final ConnectionInterceptor next;
 
-    private final ConnectionInterceptor next;
+//    private Timer timer = PoolIdleReleaserTimer.getTimer();
 
-    private FIFOSemaphore permits;
+//    private FIFOSemaphore permits;
 
     private HashMap pool;
 
     private int maxSize;
 
-    private int blockingTimeout;
-    private int actualConnections = 0;
+//    private int blockingTimeoutMilliseconds;
+
+//    private long idleTimeoutMilliseconds;
+
+//    private int connectionCount = 0;
+
+//    private int minSize = 0;
 
-    public SinglePoolMatchAllConnectionInterceptor(
-            final ConnectionInterceptor next,
-            int maxSize,
-            int blockingTimeout) {
-        this.next = next;
+//    private IdleReleaser idleReleaser;
+//    private int shrinkLater = 0;
+
+    public SinglePoolMatchAllConnectionInterceptor(final ConnectionInterceptor next,
+                                                   int maxSize,
+                                                   int minSize,
+                                                   int blockingTimeoutMilliseconds,
+                                                   int idleTimeoutMinutes) {
+
+        super(next, maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes);
+//        this.next = next;
         this.maxSize = maxSize;
-        this.blockingTimeout = blockingTimeout;
-        permits = new FIFOSemaphore(maxSize);
+//        this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
+//        permits = new FIFOSemaphore(maxSize);
         pool = new HashMap(maxSize);
+//        setIdleTimeoutMinutes(idleTimeoutMinutes);
     }
 
-    public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
-        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
-        if (mci.getManagedConnection() != null) {
-            return;
-        }
-        ManagedConnectionFactory managedConnectionFactory = mci.getManagedConnectionFactory();
-        try {
-            if (permits.attempt(blockingTimeout)) {
-                synchronized (pool) {
-                    try {
-                        if (!pool.isEmpty()) {
-                            ManagedConnection matchedMC =
-                                    managedConnectionFactory
-                                    .matchManagedConnections(
-                                            pool.keySet(),
-                                            mci.getSubject(),
-                                            mci.getConnectionRequestInfo());
-                            if (matchedMC != null) {
-                                connectionInfo.setManagedConnectionInfo((ManagedConnectionInfo) pool.get(matchedMC));
-                                pool.remove(matchedMC);
-                                if (log.isTraceEnabled()) {
-                                    log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo());
-                                }
-                                return;
-                            }
-                            //matching failed or pool is empty
-                            //if pool is at maximum size, pick a cx to kill
-                            if (actualConnections == maxSize) {
-                                Iterator iterator = pool.entrySet().iterator();
-                                ManagedConnectionInfo kill = (ManagedConnectionInfo) ((Map.Entry) iterator.next()).getValue();
-                                iterator.remove();
-                                ConnectionInfo killInfo = new ConnectionInfo(kill);
-                                returnConnection(killInfo, ConnectionReturnAction.DESTROY);
-                            }
-                            next.getConnection(connectionInfo);
-                            actualConnections++;
-                            if (log.isTraceEnabled()) {
-                                log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo());
-                            }
-                            return;
+//    public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
+//        if (connectionInfo.getManagedConnectionInfo().getManagedConnection() != null) {
+//            return;
+//        }
+//        try {
+//            if (permits.attempt(blockingTimeoutMilliseconds)) {
+//                internalGetConnection(connectionInfo);
+//            } else {
+//                throw new ResourceException("No ManagedConnections available "
+//                        + "within configured blocking timeout ( "
+//                        + blockingTimeoutMilliseconds
+//                        + " [ms] )");
+//
+//            } // end of else
+//
+//        } catch (InterruptedException ie) {
+//            throw new ResourceException("Interrupted while requesting permit!");
+//        } // end of try-catch
+//    }
+
+    protected void internalGetConnection(ConnectionInfo connectionInfo) throws ResourceException {
+        synchronized (pool) {
+            try {
+                if (!pool.isEmpty()) {
+                    ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
+                    ManagedConnectionFactory managedConnectionFactory = mci.getManagedConnectionFactory();
+                    ManagedConnection matchedMC =
+                            managedConnectionFactory
+                            .matchManagedConnections(pool.keySet(),
+                                    mci.getSubject(),
+                                    mci.getConnectionRequestInfo());
+                    if (matchedMC != null) {
+                        connectionInfo.setManagedConnectionInfo((ManagedConnectionInfo) pool.get(matchedMC));
+                        pool.remove(matchedMC);
+                        if (log.isTraceEnabled()) {
+                            log.trace("Returning pooled connection " + connectionInfo.getManagedConnectionInfo());
+                        }
+                        if (connectionCount < minSize) {
+                            timer.schedule(new FillTask(connectionInfo), 10);
                         }
-                    } catch (ResourceException e) {
-                        //something is wrong: rethrow, release permit
-                        permits.release();
-                        throw e;
+                        return;
                     }
                 }
-            } else {
-                throw new ResourceException(
-                        "No ManagedConnections available "
-                        + "within configured blocking timeout ( "
-                        + blockingTimeout
-                        + " [ms] )");
-
-            } // end of else
-
-        } catch (InterruptedException ie) {
-            throw new ResourceException("Interrupted while requesting permit!");
-        } // end of try-catch
-    }
-
-    public void returnConnection(
-            ConnectionInfo connectionInfo,
-            ConnectionReturnAction connectionReturnAction) {
-        if (log.isTraceEnabled()) {
-            log.trace("returning connection" + connectionInfo.getConnectionHandle());
-        }
-        boolean wasInPool = false;
-        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
-        if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
-            synchronized (pool) {
-                wasInPool = (pool.remove(mci.getManagedConnection()) != null);
-            }
-        } else {
-            if (mci.hasConnectionHandles()) {
-                return;
+                //matching failed or pool is empty
+                //if pool is at maximum size, pick a cx to kill
+                if (connectionCount == maxSize) {
+                    Iterator iterator = pool.entrySet().iterator();
+                    ManagedConnectionInfo kill = (ManagedConnectionInfo) ((Map.Entry) iterator.next()).getValue();
+                    iterator.remove();
+                    ConnectionInfo killInfo = new ConnectionInfo(kill);
+                    internalReturn(killInfo, ConnectionReturnAction.DESTROY);
+                }
+                next.getConnection(connectionInfo);
+                connectionCount++;
+                if (log.isTraceEnabled()) {
+                    log.trace("Returning new connection " + connectionInfo.getManagedConnectionInfo());
+                }
+                if (connectionCount < minSize) {
+                    timer.schedule(new FillTask(connectionInfo), 10);
+                }
+
+            } catch (ResourceException e) {
+                //something is wrong: rethrow, release permit
+                permits.release();
+                throw e;
             }
-        } // end of else
+        }
+    }
 
+//    public void returnConnection(ConnectionInfo connectionInfo,
+//                                 ConnectionReturnAction connectionReturnAction) {
+//        if (log.isTraceEnabled()) {
+//            log.trace("returning connection" + connectionInfo.getConnectionHandle());
+//        }
+//        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
+//        if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE && mci.hasConnectionHandles()) {
+//            return;
+//        }
+//
+//        boolean wasInPool = internalReturn(connectionInfo, connectionReturnAction);
+//
+//        if (!wasInPool) {
+//            permits.release();
+//        }
+//    }
+
+    protected boolean internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
+        ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
         ManagedConnection mc = mci.getManagedConnection();
         try {
             mc.cleanup();
@@ -151,24 +171,30 @@
             connectionReturnAction = ConnectionReturnAction.DESTROY;
         }
 
-        if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
-            actualConnections--;
-            next.returnConnection(connectionInfo, connectionReturnAction);
-        } else {
-            synchronized (pool) {
+        boolean wasInPool = false;
+        synchronized (pool) {
+            if (shrinkLater > 0) {
+                //nothing can get in the pool while shrinkLater > 0, so wasInPool is false here.
+                connectionReturnAction = ConnectionReturnAction.DESTROY;
+                shrinkLater--;
+            } else if (connectionReturnAction == ConnectionReturnAction.RETURN_HANDLE) {
                 mci.setLastUsed(System.currentTimeMillis());
                 pool.put(mci.getManagedConnection(), mci);
+                return wasInPool;
+            } else {
+                wasInPool = pool.remove(mci.getManagedConnection()) != null;
             }
         }
-        if (!wasInPool) {
-            permits.release();
-        }
+        //we must destroy connection.
+        next.returnConnection(connectionInfo, connectionReturnAction);
+        connectionCount--;
+        return wasInPool;
     }
 
     //PoolingAttributes implementation
-    public int getPartitionCount() {
-        return 1;
-    }
+//    public int getPartitionCount() {
+//        return 1;
+//    }
 
     public int getPartitionMaxSize() {
         return maxSize;
@@ -178,7 +204,136 @@
         return pool.size();
     }
 
-    public int getConnectionCount() {
-        return actualConnections;
+    protected void transferConnections(int maxSize, int shrinkNow) {
+        //1st example: copy 0 (none)
+        //2nd example: copy 10 (all)
+        HashMap oldPool = pool;
+        pool = new HashMap(maxSize);
+        //since we have replaced pool already, pool.remove will be very fast:-)
+        assert oldPool.size() == connectionCount;
+        Iterator it = oldPool.entrySet().iterator();
+        for (int i = 0; i < shrinkNow; i++) {
+            ConnectionInfo killInfo = new ConnectionInfo((ManagedConnectionInfo) ((Map.Entry)it.next()).getValue());
+            internalReturn(killInfo, ConnectionReturnAction.DESTROY);
+        }
+        for (; it.hasNext(); ) {
+            Map.Entry entry = (Map.Entry) it.next();
+            pool.put(entry.getKey(), entry.getValue());
+        }
+
     }
+
+    protected void getExpiredManagedConnectionInfos(long threshold, ArrayList killList) {
+        synchronized (pool) {
+            for (Iterator iterator = pool.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                ManagedConnectionInfo mci = (ManagedConnectionInfo) entry.getValue();
+                if (mci.getLastUsed() < threshold) {
+                    killList.add(mci);
+                }
+            }
+        }
+
+    }
+
+    protected boolean addToPool(ManagedConnectionInfo mci) {
+        boolean added;
+        synchronized (pool) {
+            connectionCount++;
+            added = getPartitionMaxSize() > getIdleConnectionCount();
+            if (added) {
+                pool.put(mci.getManagedConnection(), mci);
+            }
+        }
+        return added;
+    }
+
+//    public int getConnectionCount() {
+//        return connectionCount;
+//    }
+
+//    public int getBlockingTimeoutMilliseconds() {
+//        return blockingTimeoutMilliseconds;
+//    }
+//
+//    public void setBlockingTimeoutMilliseconds(int timeoutMilliseconds) {
+//        this.blockingTimeoutMilliseconds = timeoutMilliseconds;
+//    }
+
+//    public int getIdleTimeoutMinutes() {
+//        return (int) idleTimeoutMilliseconds / (1000 * 60);
+//    }
+
+//    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+//        this.idleTimeoutMilliseconds = idleTimeoutMinutes * 60 * 1000;
+//        if (idleReleaser != null) {
+//            idleReleaser.cancel();
+//        }
+//        idleReleaser = new IdleReleaser();
+//        timer.schedule(idleReleaser, this.idleTimeoutMilliseconds, this.idleTimeoutMilliseconds);
+//    }
+
+
+//    private class IdleReleaser extends TimerTask {
+//
+//        public void run() {
+//            long threshold = System.currentTimeMillis() - idleTimeoutMilliseconds;
+//            ManagedConnectionInfo[] killList = new ManagedConnectionInfo[pool.size()];
+//            int j = 0;
+//            synchronized (pool) {
+//                for (Iterator iterator = pool.entrySet().iterator(); iterator.hasNext();) {
+//                    Map.Entry entry = (Map.Entry) iterator.next();
+//                    ManagedConnectionInfo mci = (ManagedConnectionInfo) entry.getValue();
+//                    if (mci.getLastUsed() < threshold) {
+//                        killList[j] = mci;
+//                        j++;
+//                    }
+//                }
+//            }
+//            for (int i = 0; i < j; i++) {
+//                ManagedConnectionInfo managedConnectionInfo = killList[i];
+//                ConnectionInfo killInfo = new ConnectionInfo(managedConnectionInfo);
+//                internalReturn(killInfo, ConnectionReturnAction.DESTROY);
+//            }
+//            permits.release(j);
+//        }
+//    }
+//
+//    private class FillTask extends TimerTask {
+//        private final ManagedConnectionFactory managedConnectionFactory;
+//        private final Subject subject;
+//        private final ConnectionRequestInfo cri;
+//
+//        public FillTask(ConnectionInfo connectionInfo) {
+//            managedConnectionFactory = connectionInfo.getManagedConnectionInfo().getManagedConnectionFactory();
+//            subject = connectionInfo.getManagedConnectionInfo().getSubject();
+//            cri = connectionInfo.getManagedConnectionInfo().getConnectionRequestInfo();
+//        }
+//
+//        public void run() {
+//            while (connectionCount < minSize) {
+//                ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, cri);
+//                mci.setSubject(subject);
+//                ConnectionInfo ci = new ConnectionInfo(mci);
+//                try {
+//                    next.getConnection(ci);
+//                } catch (ResourceException e) {
+//                    return;
+//                }
+//                boolean added = false;
+//                synchronized (pool) {
+//                    connectionCount++;
+//                    added = maxSize > pool.size();
+//                    if (added) {
+//                        pool.put(mci.getManagedConnection(), mci);
+//                    }
+//                }
+//                if (!added) {
+//                    internalReturn(ci, ConnectionReturnAction.DESTROY);
+//                    return;
+//                }
+//            }
+//        }
+//    }
+
 }

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/NoPool.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/NoPool.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/NoPool.java	Mon Nov  1 09:34:13 2004
@@ -34,15 +34,43 @@
         return 0;
     }
 
+    public int getIdleConnectionCount() {
+        return 0;
+    }
+
+    public int getConnectionCount() {
+        return 0;
+    }
+
     public int getPartitionMaxSize() {
         return 0;
     }
 
-    public int getIdleConnectionCount() {
+    public void setPartitionMaxSize(int maxSize) {
+
+    }
+
+    public int getPartitionMinSize() {
         return 0;
     }
 
-    public int getConnectionCount() {
+    public void setPartitionMinSize(int minSize) {
+
+    }
+
+    public int getBlockingTimeoutMilliseconds() {
+        return 0;
+    }
+
+    public void setBlockingTimeoutMilliseconds(int timeoutMilliseconds) {
+
+    }
+
+    public int getIdleTimeoutMinutes() {
         return 0;
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+
     }
 }

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/PartitionedPool.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/PartitionedPool.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/PartitionedPool.java	Mon Nov  1 09:34:13 2004
@@ -36,8 +36,8 @@
 
     private PoolingAttributes poolingAttributes;
 
-    public PartitionedPool(boolean partitionByConnectionRequestInfo, boolean partitionBySubject, int maxSize, int blockingTimeoutMilliseconds, boolean matchOne, boolean matchAll, boolean selectOneAssumeMatch) {
-        singlePool = new SinglePool(maxSize, blockingTimeoutMilliseconds, matchOne, matchAll, selectOneAssumeMatch);
+    public PartitionedPool(int maxSize, int minSize, int blockingTimeoutMilliseconds, int idleTimeoutMinutes, boolean matchOne, boolean matchAll, boolean selectOneAssumeMatch, boolean partitionByConnectionRequestInfo, boolean partitionBySubject) {
+        singlePool = new SinglePool(maxSize, minSize, blockingTimeoutMilliseconds, idleTimeoutMinutes, matchOne, matchAll, selectOneAssumeMatch);
         this.partitionByConnectionRequestInfo = partitionByConnectionRequestInfo;
         this.partitionBySubject = partitionBySubject;
     }
@@ -67,11 +67,19 @@
     }
 
     public int getBlockingTimeoutMilliseconds() {
-        return singlePool.getBlockingTimeoutMilliseconds();
+        return poolingAttributes.getBlockingTimeoutMilliseconds();
     }
 
     public void setBlockingTimeoutMilliseconds(int blockingTimeoutMilliseconds) {
-        singlePool.setBlockingTimeoutMilliseconds(blockingTimeoutMilliseconds);
+        poolingAttributes.setBlockingTimeoutMilliseconds(blockingTimeoutMilliseconds);
+    }
+
+    public int getIdleTimeoutMinutes() {
+        return poolingAttributes.getIdleTimeoutMinutes();
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+        poolingAttributes.setIdleTimeoutMinutes(idleTimeoutMinutes);
     }
 
     public boolean isMatchOne() {
@@ -114,6 +122,18 @@
 
     public int getPartitionMaxSize() {
         return poolingAttributes.getPartitionMaxSize();
+    }
+
+    public void setPartitionMaxSize(int maxSize) throws InterruptedException {
+        poolingAttributes.setPartitionMaxSize(maxSize);
+    }
+
+    public int getPartitionMinSize() {
+        return poolingAttributes.getPartitionMinSize();
+    }
+
+    public void setPartitionMinSize(int minSize) {
+        poolingAttributes.setPartitionMinSize(minSize);
     }
 
     public int getIdleConnectionCount() {

Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/SinglePool.java
==============================================================================
--- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/SinglePool.java	(original)
+++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/connectionmanagerconfig/SinglePool.java	Mon Nov  1 09:34:13 2004
@@ -23,23 +23,24 @@
 import org.apache.geronimo.connector.outbound.PoolingAttributes;
 
 /**
- *
- *
  * @version $Rev$ $Date$
- *
- * */
+ */
 public class SinglePool implements PoolingSupport {
     private int maxSize;
+    private int minSize;
     private int blockingTimeoutMilliseconds;
+    private int idleTimeoutMinutes;
     private boolean matchOne;
     private boolean matchAll;
     private boolean selectOneAssumeMatch;
 
     private PoolingAttributes pool;
 
-    public SinglePool(int maxSize, int blockingTimeoutMilliseconds, boolean matchOne, boolean matchAll, boolean selectOneAssumeMatch) {
+    public SinglePool(int maxSize, int minSize, int blockingTimeoutMilliseconds, int idleTimeoutMinutes, boolean matchOne, boolean matchAll, boolean selectOneAssumeMatch) {
         this.maxSize = maxSize;
+        this.minSize = minSize;
         this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
+        this.idleTimeoutMinutes = idleTimeoutMinutes;
         this.matchOne = matchOne;
         this.matchAll = matchAll;
         this.selectOneAssumeMatch = selectOneAssumeMatch;
@@ -53,12 +54,34 @@
         this.maxSize = maxSize;
     }
 
+    public int getMinSize() {
+        return minSize;
+    }
+
+    public void setMinSize(int minSize) {
+        this.minSize = minSize;
+    }
+
     public int getBlockingTimeoutMilliseconds() {
         return blockingTimeoutMilliseconds;
     }
 
     public void setBlockingTimeoutMilliseconds(int blockingTimeoutMilliseconds) {
         this.blockingTimeoutMilliseconds = blockingTimeoutMilliseconds;
+        if (pool != null) {
+            pool.setBlockingTimeoutMilliseconds(blockingTimeoutMilliseconds);
+        }
+    }
+
+    public int getIdleTimeoutMinutes() {
+        return idleTimeoutMinutes;
+    }
+
+    public void setIdleTimeoutMinutes(int idleTimeoutMinutes) {
+        this.idleTimeoutMinutes = idleTimeoutMinutes;
+        if (pool != null) {
+            pool.setIdleTimeoutMinutes(idleTimeoutMinutes);
+        }
     }
 
     public boolean isMatchOne() {
@@ -87,18 +110,20 @@
 
     public ConnectionInterceptor addPoolingInterceptors(ConnectionInterceptor tail) {
         if (isMatchAll()) {
-            SinglePoolMatchAllConnectionInterceptor pool = new SinglePoolMatchAllConnectionInterceptor(
-                    tail,
+            SinglePoolMatchAllConnectionInterceptor pool = new SinglePoolMatchAllConnectionInterceptor(tail,
                     getMaxSize(),
-                    getBlockingTimeoutMilliseconds());
+                    getMinSize(),
+                    getBlockingTimeoutMilliseconds(),
+                    getIdleTimeoutMinutes());
             this.pool = pool;
             return pool;
 
         } else {
-            SinglePoolConnectionInterceptor pool = new SinglePoolConnectionInterceptor(
-                    tail,
+            SinglePoolConnectionInterceptor pool = new SinglePoolConnectionInterceptor(tail,
                     getMaxSize(),
+                    getMinSize(),
                     getBlockingTimeoutMilliseconds(),
+                    getIdleTimeoutMinutes(),
                     isSelectOneAssumeMatch());
             this.pool = pool;
             return pool;
@@ -110,14 +135,32 @@
     }
 
     public int getPartitionMaxSize() {
-        return pool == null?  maxSize: pool.getPartitionMaxSize();
+        return maxSize;
+    }
+
+    public void setPartitionMaxSize(int maxSize) throws InterruptedException {
+        if (pool != null) {
+            pool.setPartitionMaxSize(maxSize);
+        }
+        this.maxSize = maxSize;
+    }
+
+    public int getPartitionMinSize() {
+        return minSize;
+    }
+
+    public void setPartitionMinSize(int minSize) {
+        if (pool != null) {
+            pool.setPartitionMinSize(minSize);
+        }
+        this.minSize = minSize;
     }
 
     public int getIdleConnectionCount() {
-        return pool.getIdleConnectionCount();
+        return pool == null ? 0 : pool.getIdleConnectionCount();
     }
 
     public int getConnectionCount() {
-        return pool.getConnectionCount();
+        return pool == null ? 0 : pool.getConnectionCount();
     }
 }

Modified: geronimo/trunk/modules/connector/src/schema/geronimo-connector_1_5.xsd
==============================================================================
--- geronimo/trunk/modules/connector/src/schema/geronimo-connector_1_5.xsd	(original)
+++ geronimo/trunk/modules/connector/src/schema/geronimo-connector_1_5.xsd	Mon Nov  1 09:34:13 2004
@@ -287,8 +287,10 @@
 
     <xsd:complexType name="singlepool-Type">
         <xsd:sequence>
-            <xsd:element name="max-size" type="xsd:int"/>
-            <xsd:element name="blocking-timeout-milliseconds" type="xsd:int"/>
+            <xsd:element name="max-size" type="xsd:int" minOccurs="0"/>
+            <xsd:element name="min-size" type="xsd:int" minOccurs="0"/>
+            <xsd:element name="blocking-timeout-milliseconds" type="xsd:int" minOccurs="0"/>
+            <xsd:element name="idle-timeout-minutes" type="xsd:int" minOccurs="0"/>
             <xsd:choice>
                 <xsd:element name="match-one"/>
                 <xsd:element name="match-all"/>

Modified: geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_0ConfigBuilderTest.java
==============================================================================
--- geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_0ConfigBuilderTest.java	(original)
+++ geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_0ConfigBuilderTest.java	Mon Nov  1 09:34:13 2004
@@ -176,7 +176,7 @@
         ObjectName connectionTrackerName = new ObjectName("geronimo.connector:service=ConnectionTracker");
 
         Kernel kernel = new Kernel("testServer");
-        ConnectorModuleBuilder moduleBuilder = new ConnectorModuleBuilder(kernel);
+        ConnectorModuleBuilder moduleBuilder = new ConnectorModuleBuilder(10, 0, 5000, 15, kernel);
         File rarFile = action.getRARFile();
 
         ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
@@ -230,7 +230,7 @@
         JarFile rarFile = null;
         try {
             rarFile = DeploymentUtil.createJarFile(new File(basedir, "target/test-ear-noger.ear"));
-            EARConfigBuilder configBuilder = new EARConfigBuilder(j2eeServer, null, connectionTrackerName, null, null, null, null, null, null, new ConnectorModuleBuilder(kernel), null, null, kernel);
+            EARConfigBuilder configBuilder = new EARConfigBuilder(j2eeServer, null, connectionTrackerName, null, null, null, null, null, null, new ConnectorModuleBuilder(10, 0, 5000, 15, kernel), null, null, kernel);
             File tempDir = null;
             try {
                 tempDir = DeploymentUtil.createTempDir();

Modified: geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_5ConfigBuilderTest.java
==============================================================================
--- geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_5ConfigBuilderTest.java	(original)
+++ geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/deployment/RAR_1_5ConfigBuilderTest.java	Mon Nov  1 09:34:13 2004
@@ -121,7 +121,7 @@
         ObjectName connectionTrackerName = new ObjectName("geronimo.connector:service=ConnectionTracker");
 
         Kernel kernel = new Kernel("testServer");
-        ConnectorModuleBuilder moduleBuilder = new ConnectorModuleBuilder(kernel);
+        ConnectorModuleBuilder moduleBuilder = new ConnectorModuleBuilder(10, 0, 5000, 15, kernel);
         File rarFile = action.getRARFile();
 
         ClassLoader oldCl = Thread.currentThread().getContextClassLoader();

Modified: geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/ConnectionManagerTestUtils.java
==============================================================================
--- geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/ConnectionManagerTestUtils.java	(original)
+++ geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/ConnectionManagerTestUtils.java	Mon Nov  1 09:34:13 2004
@@ -53,7 +53,9 @@
     protected boolean useThreadCaching = false;
     protected boolean useTransactions = true;
     protected int maxSize = 10;
+    protected int minSize = 0;
     protected int blockingTimeout = 100;
+    protected int idleTimeoutMinutes = 15;
     protected boolean useConnectionRequestInfo = false;
     protected boolean useSubject = true;
     private boolean matchOne = true;
@@ -75,7 +77,7 @@
     protected Subject subject;
     protected UserTransactionImpl userTransaction;
     protected TransactionSupport transactionSupport = new XATransactions(useTransactionCaching, useThreadCaching);
-    protected PoolingSupport poolingSupport = new PartitionedPool(useConnectionRequestInfo, useSubject, maxSize, blockingTimeout, matchOne, matchAll, selectOneNoMatch);
+    protected PoolingSupport poolingSupport = new PartitionedPool(maxSize, minSize, blockingTimeout, idleTimeoutMinutes, matchOne, matchAll, selectOneNoMatch, useConnectionRequestInfo, useSubject);
 
     protected DefaultInterceptor mockComponent = new DefaultInterceptor() {
         public Object invoke(InstanceContext newInstanceContext) throws Throwable {

Modified: geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/PoolDequeTest.java
==============================================================================
--- geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/PoolDequeTest.java	(original)
+++ geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/PoolDequeTest.java	Mon Nov  1 09:34:13 2004
@@ -40,7 +40,7 @@
     public void testFill() throws Exception {
         SinglePoolConnectionInterceptor.PoolDeque pool = new SinglePoolConnectionInterceptor.PoolDeque(MAX_SIZE);
         for (int i = 0; i < MAX_SIZE; i++) {
-            pool.addLast(new ManagedConnectionInfo(null, null));
+            pool.add(new ManagedConnectionInfo(null, null));
         }
     }
 
@@ -50,11 +50,11 @@
         ManagedConnectionInfo[] mcis = new ManagedConnectionInfo[MAX_SIZE];
         for (int i = 0; i < MAX_SIZE; i++) {
             mcis[i] = new ManagedConnectionInfo(null, null);
-            pool.addLast(mcis[i]);
+            pool.add(mcis[i]);
         }
 
         for (int i = MAX_SIZE - 1; i >= 0; i--) {
-            assertTrue("Expected to get corresponding MCI from pool", mcis[i] == pool.peekLast());
+            assertTrue("Expected to get corresponding MCI from pool", mcis[i] == pool.peek(i));
             assertTrue("Expected to get corresponding MCI from pool", mcis[i] == pool.removeLast());
         }
         assertTrue("Expected pool to be empty!", pool.isEmpty());
@@ -65,7 +65,7 @@
         ManagedConnectionInfo[] mcis = new ManagedConnectionInfo[MAX_SIZE];
         for (int i = 0; i < MAX_SIZE; i++) {
             mcis[i] = new ManagedConnectionInfo(null, null);
-            pool.addLast(mcis[i]);
+            pool.add(mcis[i]);
         }
 
         for (int i = 0; i < MAX_SIZE; i++) {