You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ma...@apache.org on 2013/07/24 23:58:57 UTC

svn commit: r1506741 - in /commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2: ./ datasources/ managed/

Author: markt
Date: Wed Jul 24 21:58:57 2013
New Revision: 1506741

URL: http://svn.apache.org/r1506741
Log:
DBCP-156
Implement a configuration option - disabled by default - that sets a maximum permitted lifetime for a connection after which the connection will automatically fail the next validation, activation or passivation.

Modified:
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/LocalStrings.properties
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/managed/BasicManagedDataSource.java

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSource.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSource.java Wed Jul 24 21:58:57 2013
@@ -1103,6 +1103,31 @@ public class BasicDataSource implements 
         this.restartNeeded = true;
     }
 
+
+    private long maxConnLifetimeMillis = -1;
+
+    /**
+     * Returns the maximum permitted lifetime of a connection in milliseconds. A
+     * value of zero or less indicates an infinite lifetime.
+     */
+    public long getMaxConnLifetimeMillis() {
+        return maxConnLifetimeMillis;
+    }
+
+    /**
+     * <p>Sets the maximum permitted lifetime of a connection in
+     * milliseconds. A value of zero or less indicates an infinite lifetime.</p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been
+     * initialized.  The pool is initialized the first time one of the
+     * following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
+     */
+    public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
+
     // ----------------------------------------------------- Instance Variables
 
     // TODO: review & make isRestartNeeded() public, restartNeeded protected
@@ -1833,6 +1858,7 @@ public class BasicDataSource implements 
             connectionFactory.setPoolStatements(poolPreparedStatements);
             connectionFactory.setMaxOpenPrepatedStatements(
                     maxOpenPreparedStatements);
+            connectionFactory.setMaxConnLifetimeMillis(maxConnLifetimeMillis);
             validateConnectionFactory(connectionFactory);
         } catch (RuntimeException e) {
             throw e;

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java Wed Jul 24 21:58:57 2013
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You 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.
@@ -84,6 +84,7 @@ public class BasicDataSourceFactory impl
     private final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
     private final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
     private final static String PROP_CONNECTIONPROPERTIES = "connectionProperties";
+    private final static String PROP_MAXCONNLIFETIMEMILLIS = "maxConnLifetimeMillis";
 
     private final static String[] ALL_PROPERTIES = {
         PROP_DEFAULTAUTOCOMMIT,
@@ -118,7 +119,8 @@ public class BasicDataSourceFactory impl
         PROP_LOGABANDONED,
         PROP_POOLPREPAREDSTATEMENTS,
         PROP_MAXOPENPREPAREDSTATEMENTS,
-        PROP_CONNECTIONPROPERTIES
+        PROP_CONNECTIONPROPERTIES,
+        PROP_MAXCONNLIFETIMEMILLIS
     };
 
     // -------------------------------------------------- ObjectFactory Methods
@@ -169,7 +171,7 @@ public class BasicDataSourceFactory impl
     /**
      * Creates and configures a {@link BasicDataSource} instance based on the
      * given properties.
-     * 
+     *
      * @param properties the datasource configuration properties
      * @throws Exception if an error occurs creating the data source
      */
@@ -237,7 +239,7 @@ public class BasicDataSourceFactory impl
         if (value != null) {
             dataSource.setLifo(Boolean.valueOf(value).booleanValue());
         }
-        
+
         value = properties.getProperty(PROP_MAXACTIVE);
         if (value != null) {
             dataSource.setMaxTotal(Integer.parseInt(value));
@@ -287,7 +289,7 @@ public class BasicDataSourceFactory impl
         if (value != null) {
             dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
         }
-        
+
         value = properties.getProperty(PROP_SOFTMINEVICTABLEIDLETIMEMILLIS);
         if (value != null) {
             dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
@@ -322,7 +324,7 @@ public class BasicDataSourceFactory impl
         if (value != null) {
             dataSource.setValidationQueryTimeout(Integer.parseInt(value));
         }
-        
+
         value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
         if (value != null) {
             dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
@@ -339,7 +341,7 @@ public class BasicDataSourceFactory impl
         }
 
         value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
-        if (value != null) {     
+        if (value != null) {
             dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
         }
 
@@ -381,6 +383,11 @@ public class BasicDataSourceFactory impl
           }
         }
 
+        value = properties.getProperty(PROP_MAXCONNLIFETIMEMILLIS);
+        if (value != null) {
+            dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
+        }
+
         // DBCP-215
         // Trick to make sure that initialSize connections are created
         if (dataSource.getInitialSize() > 0) {

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/LocalStrings.properties
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/LocalStrings.properties?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/LocalStrings.properties (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/LocalStrings.properties Wed Jul 24 21:58:57 2013
@@ -13,4 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+connectionFactory.lifetimeExceeded=The lifetime of the connection [{0}] milliseconds exceeds the maximum permitted value of [{1}] milliseconds
+
 poolableConnectionFactory.validateObject.fail=Failed to validate a poolable connection
\ No newline at end of file

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java Wed Jul 24 21:58:57 2013
@@ -167,6 +167,16 @@ public class PoolableConnectionFactory
         this.maxOpenPreparedStatements = maxOpenPreparedStatements;
     }
 
+    /**
+     * Sets the maximum lifetime in milliseconds of a connection after which the
+     * connection will always fail activation, passivation and validation. A
+     * value of zero or less indicates an infinite lifetime. The default value
+     * is -1.
+     */
+    public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
     @Override
     public PooledObject<PoolableConnection> makeObject() throws Exception {
         Connection conn = _connFactory.createConnection();
@@ -228,9 +238,11 @@ public class PoolableConnectionFactory
     @Override
     public boolean validateObject(PooledObject<PoolableConnection> p) {
         try {
+            validateLifetime(p);
+
             validateConnection(p.getObject());
             return true;
-        } catch(Exception e) {
+        } catch (Exception e) {
             if (log.isDebugEnabled()) {
                 log.debug(Utils.getMessage(
                         "poolableConnectionFactory.validateObject.fail"), e);
@@ -278,6 +290,9 @@ public class PoolableConnectionFactory
     @Override
     public void passivateObject(PooledObject<PoolableConnection> p)
             throws Exception {
+
+        validateLifetime(p);
+
         PoolableConnection conn = p.getObject();
         if(!conn.getAutoCommit() && !conn.isReadOnly()) {
             conn.rollback();
@@ -294,6 +309,8 @@ public class PoolableConnectionFactory
     public void activateObject(PooledObject<PoolableConnection> p)
             throws Exception {
 
+        validateLifetime(p);
+
         PoolableConnection conn = p.getObject();
         conn.activate();
 
@@ -315,6 +332,20 @@ public class PoolableConnectionFactory
         }
     }
 
+    private void validateLifetime(PooledObject<PoolableConnection> p)
+            throws Exception {
+        if (maxConnLifetimeMillis > 0) {
+            long lifetime = System.currentTimeMillis() - p.getCreateTime();
+            if (lifetime > maxConnLifetimeMillis) {
+                throw new Exception(Utils.getMessage(
+                        "connectionFactory.lifetimeExceeded",
+                        Long.valueOf(lifetime),
+                        Long.valueOf(maxConnLifetimeMillis)));
+            }
+        }
+    }
+
+
     protected volatile ConnectionFactory _connFactory = null;
     protected volatile String _validationQuery = null;
     protected volatile int _validationQueryTimeout = -1;
@@ -328,6 +359,7 @@ public class PoolableConnectionFactory
     protected boolean poolStatements = false;
     protected int maxOpenPreparedStatements =
         GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
+    private long maxConnLifetimeMillis = -1;
 
     /**
      * Internal constant to indicate the level is not set.

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java Wed Jul 24 21:58:57 2013
@@ -30,6 +30,7 @@ import javax.sql.ConnectionEventListener
 import javax.sql.ConnectionPoolDataSource;
 import javax.sql.PooledConnection;
 
+import org.apache.commons.dbcp2.PoolableConnection;
 import org.apache.commons.dbcp2.Utils;
 import org.apache.commons.pool2.ObjectPool;
 import org.apache.commons.pool2.PoolableObjectFactory;
@@ -58,6 +59,8 @@ class CPDSConnectionFactory
     private ObjectPool<PooledConnectionAndInfo> _pool;
     private final String _username;
     private String _password = null;
+    private long maxConnLifetimeMillis = -1;
+
 
     /**
      * Map of PooledConnections for which close events are ignored.
@@ -157,6 +160,11 @@ class CPDSConnectionFactory
 
     @Override
     public boolean validateObject(PooledObject<PooledConnectionAndInfo> p) {
+        try {
+            validateLifetime(p);
+        } catch (Exception e) {
+            return false;
+        }
         boolean valid = false;
         PooledConnection pconn = p.getObject().getPooledConnection();
         String query = _validationQuery;
@@ -196,11 +204,15 @@ class CPDSConnectionFactory
     }
 
     @Override
-    public void passivateObject(PooledObject<PooledConnectionAndInfo> p) {
+    public void passivateObject(PooledObject<PooledConnectionAndInfo> p)
+            throws Exception {
+        validateLifetime(p);
     }
 
     @Override
-    public void activateObject(PooledObject<PooledConnectionAndInfo> p) {
+    public void activateObject(PooledObject<PooledConnectionAndInfo> p)
+            throws Exception {
+        validateLifetime(p);
     }
 
     // ***********************************************************************
@@ -302,6 +314,16 @@ class CPDSConnectionFactory
     }
 
     /**
+     * Sets the maximum lifetime in milliseconds of a connection after which the
+     * connection will always fail activation, passivation and validation. A
+     * value of zero or less indicates an infinite lifetime. The default value
+     * is -1.
+     */
+    public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
+    /**
      * Verifies that the username matches the user whose connections are being managed by this
      * factory and closes the pool if this is the case; otherwise does nothing.
      */
@@ -319,4 +341,16 @@ class CPDSConnectionFactory
         }
     }
 
+    private void validateLifetime(PooledObject<PooledConnectionAndInfo> p)
+            throws Exception {
+        if (maxConnLifetimeMillis > 0) {
+            long lifetime = System.currentTimeMillis() - p.getCreateTime();
+            if (lifetime > maxConnLifetimeMillis) {
+                throw new Exception(Utils.getMessage(
+                        "connectionFactory.lifetimeExceeded",
+                        Long.valueOf(lifetime),
+                        Long.valueOf(maxConnLifetimeMillis)));
+            }
+        }
+    }
 }

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java Wed Jul 24 21:58:57 2013
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You 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.
@@ -39,7 +39,7 @@ import javax.sql.PooledConnection;
 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
 
 /**
- * <p>The base class for <code>SharedPoolDataSource</code> and 
+ * <p>The base class for <code>SharedPoolDataSource</code> and
  * <code>PerUserPoolDataSource</code>.  Many of the configuration properties
  * are shared and defined here.  This class is declared public in order
  * to allow particular usage with commons-beanutils; do not make direct
@@ -50,7 +50,7 @@ import org.apache.commons.pool2.impl.Gen
  * A J2EE container will normally provide some method of initializing the
  * <code>DataSource</code> whose attributes are presented
  * as bean getters/setters and then deploying it via JNDI.  It is then
- * available to an application as a source of pooled logical connections to 
+ * available to an application as a source of pooled logical connections to
  * the database.  The pool needs a source of physical connections.  This
  * source is in the form of a <code>ConnectionPoolDataSource</code> that
  * can be specified via the {@link #setDataSourceName(String)} used to
@@ -59,25 +59,25 @@ import org.apache.commons.pool2.impl.Gen
  *
  * <p>
  * Although normally used within a JNDI environment, A DataSource
- * can be instantiated and initialized as any bean.  In this case the 
+ * can be instantiated and initialized as any bean.  In this case the
  * <code>ConnectionPoolDataSource</code> will likely be instantiated in
  * a similar manner.  This class allows the physical source of connections
- * to be attached directly to this pool using the 
+ * to be attached directly to this pool using the
  * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
  * </p>
  *
  * <p>
- * The dbcp package contains an adapter, 
+ * The dbcp package contains an adapter,
  * {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS},
  * that can be used to allow the use of <code>DataSource</code>'s based on this
- * class with jdbc driver implementations that do not supply a 
+ * class with jdbc driver implementations that do not supply a
  * <code>ConnectionPoolDataSource</code>, but still
  * provide a {@link java.sql.Driver} implementation.
  * </p>
  *
  * <p>
- * The <a href="package-summary.html">package documentation</a> contains an 
- * example using Apache Tomcat and JNDI and it also contains a non-JNDI example. 
+ * The <a href="package-summary.html">package documentation</a> contains an
+ * example using Apache Tomcat and JNDI and it also contains a non-JNDI example.
  * </p>
  *
  * @author John D. McNally
@@ -86,42 +86,42 @@ import org.apache.commons.pool2.impl.Gen
 public abstract class InstanceKeyDataSource
         implements DataSource, Referenceable, Serializable {
     private static final long serialVersionUID = -4243533936955098795L;
-    private static final String GET_CONNECTION_CALLED 
-            = "A Connection was already requested from this source, " 
+    private static final String GET_CONNECTION_CALLED
+            = "A Connection was already requested from this source, "
             + "further initialization is not allowed.";
     private static final String BAD_TRANSACTION_ISOLATION
         = "The requested TransactionIsolation level is invalid.";
     /**
-    * Internal constant to indicate the level is not set. 
+    * Internal constant to indicate the level is not set.
     */
     protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
-    
+
     /** Guards property setters - once true, setters throw IllegalStateException */
     private volatile boolean getConnectionCalled = false;
 
     /** Underlying source of PooledConnections */
     private ConnectionPoolDataSource dataSource = null;
-    
+
     /** DataSource Name used to find the ConnectionPoolDataSource */
     private String dataSourceName = null;
-    
+
     // Default connection properties
     private boolean defaultAutoCommit = false;
     private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
     private boolean defaultReadOnly = false;
-    
+
     /** Description */
     private String description = null;
-    
+
     /** Environment that may be used to set up a jndi initial context. */
     Properties jndiEnvironment = null;
-    
+
     /** Login TimeOut in seconds */
     private int loginTimeout = 0;
-    
+
     /** Log stream */
     private PrintWriter logWriter = null;
-    
+
     // Pool properties
     private boolean _testOnBorrow =
         GenericObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
@@ -130,7 +130,7 @@ public abstract class InstanceKeyDataSou
     private int _timeBetweenEvictionRunsMillis = (int)
         Math.min(Integer.MAX_VALUE,
                  GenericObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
-    private int _numTestsPerEvictionRun = 
+    private int _numTestsPerEvictionRun =
         GenericObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
     private int _minEvictableIdleTimeMillis = (int)
     Math.min(Integer.MAX_VALUE,
@@ -139,7 +139,9 @@ public abstract class InstanceKeyDataSou
         GenericObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
     private String validationQuery = null;
     private boolean rollbackAfterValidation = false;
-    
+    private long maxConnLifetimeMillis = -1;
+
+
     /** true iff one of the setters for testOnBorrow, testOnReturn, testWhileIdle has been called. */
     private boolean testPositionSet = false;
 
@@ -168,7 +170,7 @@ public abstract class InstanceKeyDataSou
      * Close the connection pool being maintained by this datasource.
      */
     public abstract void close() throws Exception;
-    
+
     protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
 
     /* JDBC_4_ANT_KEY_BEGIN */
@@ -200,7 +202,7 @@ public abstract class InstanceKeyDataSou
     public ConnectionPoolDataSource getConnectionPoolDataSource() {
         return dataSource;
     }
-    
+
     /**
      * Set the backend ConnectionPoolDataSource.  This property should not be
      * set if using jndi to access the datasource.
@@ -213,7 +215,7 @@ public abstract class InstanceKeyDataSou
             throw new IllegalStateException(
                 "Cannot set the DataSource, if JNDI is used.");
         }
-        if (dataSource != null) 
+        if (dataSource != null)
         {
             throw new IllegalStateException(
                 "The CPDS has already been set. It cannot be altered.");
@@ -224,7 +226,7 @@ public abstract class InstanceKeyDataSou
 
     /**
      * Get the name of the ConnectionPoolDataSource which backs this pool.
-     * This name is used to look up the datasource from a jndi service 
+     * This name is used to look up the datasource from a jndi service
      * provider.
      *
      * @return value of dataSourceName.
@@ -232,10 +234,10 @@ public abstract class InstanceKeyDataSou
     public String getDataSourceName() {
         return dataSourceName;
     }
-    
+
     /**
      * Set the name of the ConnectionPoolDataSource which backs this pool.
-     * This name is used to look up the datasource from a jndi service 
+     * This name is used to look up the datasource from a jndi service
      * provider.
      *
      * @param v  Value to assign to dataSourceName.
@@ -247,18 +249,18 @@ public abstract class InstanceKeyDataSou
                 "Cannot set the JNDI name for the DataSource, if already " +
                 "set using setConnectionPoolDataSource.");
         }
-        if (dataSourceName != null) 
+        if (dataSourceName != null)
         {
             throw new IllegalStateException(
-                "The DataSourceName has already been set. " + 
+                "The DataSourceName has already been set. " +
                 "It cannot be altered.");
         }
         this.dataSourceName = v;
         instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
     }
 
-    /** 
-     * Get the value of defaultAutoCommit, which defines the state of 
+    /**
+     * Get the value of defaultAutoCommit, which defines the state of
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setAutoCommit(boolean).
      * The default is true.
@@ -268,9 +270,9 @@ public abstract class InstanceKeyDataSou
     public boolean isDefaultAutoCommit() {
         return defaultAutoCommit;
     }
-    
+
     /**
-     * Set the value of defaultAutoCommit, which defines the state of 
+     * Set the value of defaultAutoCommit, which defines the state of
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setAutoCommit(boolean).
      * The default is true.
@@ -283,7 +285,7 @@ public abstract class InstanceKeyDataSou
     }
 
     /**
-     * Get the value of defaultReadOnly, which defines the state of 
+     * Get the value of defaultReadOnly, which defines the state of
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setReadOnly(boolean).
      * The default is false.
@@ -293,9 +295,9 @@ public abstract class InstanceKeyDataSou
     public boolean isDefaultReadOnly() {
         return defaultReadOnly;
     }
-    
+
     /**
-     * Set the value of defaultReadOnly, which defines the state of 
+     * Set the value of defaultReadOnly, which defines the state of
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setReadOnly(boolean).
      * The default is false.
@@ -312,7 +314,7 @@ public abstract class InstanceKeyDataSou
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setTransactionIsolation(int).
      * If this method returns -1, the default is JDBC driver dependent.
-     * 
+     *
      * @return value of defaultTransactionIsolation.
      */
     public int getDefaultTransactionIsolation() {
@@ -324,7 +326,7 @@ public abstract class InstanceKeyDataSou
      * connections handed out from this pool.  The value can be changed
      * on the Connection using Connection.setTransactionIsolation(int).
      * The default is JDBC driver dependent.
-     * 
+     *
      * @param v  Value to assign to defaultTransactionIsolation
      */
     public void setDefaultTransactionIsolation(int v) {
@@ -341,7 +343,7 @@ public abstract class InstanceKeyDataSou
         }
         this.defaultTransactionIsolation = v;
     }
-    
+
     /**
      * Get the description.  This property is defined by jdbc as for use with
      * GUI (or other) tools that might deploy the datasource.  It serves no
@@ -352,18 +354,18 @@ public abstract class InstanceKeyDataSou
     public String getDescription() {
         return description;
     }
-    
+
     /**
      * Set the description.  This property is defined by jdbc as for use with
      * GUI (or other) tools that might deploy the datasource.  It serves no
      * internal purpose.
-     * 
+     *
      * @param v  Value to assign to description.
      */
     public void setDescription(String v) {
         this.description = v;
     }
-        
+
     /**
      * Get the value of jndiEnvironment which is used when instantiating
      * a jndi InitialContext.  This InitialContext is used to locate the
@@ -378,12 +380,12 @@ public abstract class InstanceKeyDataSou
         }
         return value;
     }
-    
+
     /**
      * Sets the value of the given JNDI environment property to be used when
      * instantiating a JNDI InitialContext. This InitialContext is used to
      * locate the backend ConnectionPoolDataSource.
-     * 
+     *
      * @param key the JNDI environment property to set.
      * @param value the value assigned to specified JNDI environment property.
      */
@@ -393,7 +395,7 @@ public abstract class InstanceKeyDataSou
         }
         jndiEnvironment.setProperty(key, value);
     }
-    
+
     /**
      * Get the value of loginTimeout.
      * @return value of loginTimeout.
@@ -402,7 +404,7 @@ public abstract class InstanceKeyDataSou
     public int getLoginTimeout() {
         return loginTimeout;
     }
-    
+
     /**
      * Set the value of loginTimeout.
      * @param v  Value to assign to loginTimeout.
@@ -411,7 +413,7 @@ public abstract class InstanceKeyDataSou
     public void setLoginTimeout(int v) {
         this.loginTimeout = v;
     }
-        
+
     /**
      * Get the value of logWriter.
      * @return value of logWriter.
@@ -420,10 +422,10 @@ public abstract class InstanceKeyDataSou
     public PrintWriter getLogWriter() {
         if (logWriter == null) {
             logWriter = new PrintWriter(System.out);
-        }        
+        }
         return logWriter;
     }
-    
+
     /**
      * Set the value of logWriter.
      * @param v  Value to assign to logWriter.
@@ -432,14 +434,14 @@ public abstract class InstanceKeyDataSou
     public void setLogWriter(PrintWriter v) {
         this.logWriter = v;
     }
-    
+
     /**
      * @see #getTestOnBorrow
      */
     public final boolean isTestOnBorrow() {
         return getTestOnBorrow();
     }
-    
+
     /**
      * When <tt>true</tt>, objects will be
      * {*link PoolableObjectFactory#validateObject validated}
@@ -478,7 +480,7 @@ public abstract class InstanceKeyDataSou
     public final boolean isTestOnReturn() {
         return getTestOnReturn();
     }
-    
+
     /**
      * When <tt>true</tt>, objects will be
      * {*link PoolableObjectFactory#validateObject validated}
@@ -527,7 +529,7 @@ public abstract class InstanceKeyDataSou
      *
      * @see #getTimeBetweenEvictionRunsMillis
      */
-    public void 
+    public void
         setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
         assertInitializationAllowed();
             _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
@@ -593,7 +595,7 @@ public abstract class InstanceKeyDataSou
     public final boolean isTestWhileIdle() {
         return getTestWhileIdle();
     }
-    
+
     /**
      * When <tt>true</tt>, objects will be
      * {*link PoolableObjectFactory#validateObject validated}
@@ -651,10 +653,10 @@ public abstract class InstanceKeyDataSou
     }
 
     /**
-     * Whether a rollback will be issued after executing the SQL query 
+     * Whether a rollback will be issued after executing the SQL query
      * that will be used to validate connections from this pool
      * before returning them to the caller.
-     * 
+     *
      * @return true if a rollback will be issued after executing the
      * validation query
      * @since 1.2.2
@@ -664,12 +666,12 @@ public abstract class InstanceKeyDataSou
     }
 
     /**
-     * Whether a rollback will be issued after executing the SQL query 
+     * Whether a rollback will be issued after executing the SQL query
      * that will be used to validate connections from this pool
      * before returning them to the caller. Default behavior is NOT
      * to issue a rollback. The setting will only have an effect
      * if a validation query is set
-     * 
+     *
      * @param rollbackAfterValidation new property value
      * @since 1.2.2
      */
@@ -678,11 +680,32 @@ public abstract class InstanceKeyDataSou
         this.rollbackAfterValidation = rollbackAfterValidation;
     }
 
+    /**
+     * Returns the maximum permitted lifetime of a connection in milliseconds. A
+     * value of zero or less indicates an infinite lifetime.
+     */
+    public long getMaxConnLifetimeMillis() {
+        return maxConnLifetimeMillis;
+    }
+
+    /**
+     * <p>Sets the maximum permitted lifetime of a connection in
+     * milliseconds. A value of zero or less indicates an infinite lifetime.</p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been
+     * initialized.  The pool is initialized the first time one of the
+     * following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
+     */
+    public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
     // ----------------------------------------------------------------------
     // Instrumentation Methods
 
     // ----------------------------------------------------------------------
-    // DataSource implementation 
+    // DataSource implementation
 
     /**
      * Attempt to establish a database connection.
@@ -701,14 +724,14 @@ public abstract class InstanceKeyDataSou
      * did not match the password used to create the pooled connection.  If the connection attempt succeeds, this
      * means that the database password has been changed.  In this case, the <code>PooledConnectionAndInfo</code>
      * instance retrieved with the old password is destroyed and the <code>getPooledConnectionAndInfo</code> is
-     * repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned. 
-     * 
+     * repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned.
+     *
      */
     @Override
     public Connection getConnection(String username, String password)
-            throws SQLException {        
+            throws SQLException {
         if (instanceKey == null) {
-            throw new SQLException("Must set the ConnectionPoolDataSource " 
+            throw new SQLException("Must set the ConnectionPoolDataSource "
                     + "through setDataSourceName or setConnectionPoolDataSource"
                     + " before calling getConnection.");
         }
@@ -722,15 +745,15 @@ public abstract class InstanceKeyDataSou
         } catch (RuntimeException e) {
             closeDueToException(info);
             throw e;
-        } catch (SQLException e) {            
+        } catch (SQLException e) {
             closeDueToException(info);
             throw e;
         } catch (Exception e) {
             closeDueToException(info);
             throw new SQLException("Cannot borrow connection from pool", e);
         }
-        
-        if (!(null == password ? null == info.getPassword() 
+
+        if (!(null == password ? null == info.getPassword()
                 : password.equals(info.getPassword()))) {  // Password on PooledConnectionAndInfo does not match
             try { // See if password has changed by attempting connection
                 testCPDS(username, password);
@@ -752,7 +775,7 @@ public abstract class InstanceKeyDataSou
             manager.invalidate(info.getPooledConnection()); // Destroy and remove from pool
             manager.setPassword(upkey.getPassword()); // Reset the password on the factory if using CPDSConnectionFactory
             info = null;
-            for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return 
+            for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
                 try {
                     info = getPooledConnectionAndInfo(username, password);
                 } catch (NoSuchElementException e) {
@@ -761,7 +784,7 @@ public abstract class InstanceKeyDataSou
                 } catch (RuntimeException e) {
                     closeDueToException(info);
                     throw e;
-                } catch (SQLException e) {            
+                } catch (SQLException e) {
                     closeDueToException(info);
                     throw e;
                 } catch (Exception e) {
@@ -776,21 +799,21 @@ public abstract class InstanceKeyDataSou
                     }
                     info = null;
                 }
-            }  
+            }
             if (info == null) {
                 throw new SQLException("Cannot borrow connection from pool - password change failure.");
             }
         }
 
         Connection con = info.getPooledConnection().getConnection();
-        try { 
+        try {
             setupDefaults(con, username);
             con.clearWarnings();
             return con;
-        } catch (SQLException ex) {  
+        } catch (SQLException ex) {
             try {
                 con.close();
-            } catch (Exception exc) { 
+            } catch (Exception exc) {
                 getLogWriter().println(
                      "ignoring exception during close: " + exc);
             }
@@ -798,14 +821,14 @@ public abstract class InstanceKeyDataSou
         }
     }
 
-    protected abstract PooledConnectionAndInfo 
+    protected abstract PooledConnectionAndInfo
         getPooledConnectionAndInfo(String username, String password)
         throws SQLException;
 
-    protected abstract void setupDefaults(Connection con, String username) 
+    protected abstract void setupDefaults(Connection con, String username)
         throws SQLException;
 
-        
+
     private void closeDueToException(PooledConnectionAndInfo info) {
         if (info != null) {
             try {
@@ -815,20 +838,20 @@ public abstract class InstanceKeyDataSou
                 // of handling another exception.  But record it because
                 // it potentially leaks connections from the pool.
                 getLogWriter().println("[ERROR] Could not return connection to "
-                    + "pool during exception handling. " + e.getMessage());   
+                    + "pool during exception handling. " + e.getMessage());
             }
         }
     }
 
-    protected ConnectionPoolDataSource 
+    protected ConnectionPoolDataSource
         testCPDS(String username, String password)
         throws javax.naming.NamingException, SQLException {
         // The source of physical db connections
         ConnectionPoolDataSource cpds = this.dataSource;
-        if (cpds == null) {            
+        if (cpds == null) {
             Context ctx = null;
             if (jndiEnvironment == null) {
-                ctx = new InitialContext();                
+                ctx = new InitialContext();
             } else {
                 ctx = new InitialContext(jndiEnvironment);
             }
@@ -842,7 +865,7 @@ public abstract class InstanceKeyDataSou
                     + " doesn't implement javax.sql.ConnectionPoolDataSource");
             }
         }
-        
+
         // try to get a connection with the supplied username/password
         PooledConnection conn = null;
         try {
@@ -871,7 +894,7 @@ public abstract class InstanceKeyDataSou
     }
 
     // ----------------------------------------------------------------------
-    // Referenceable implementation 
+    // Referenceable implementation
 
     /**
      * Retrieves the Reference of this object.
@@ -886,11 +909,11 @@ public abstract class InstanceKeyDataSou
      */
     // TODO: Remove the implementation of this method at next major
     // version release.
-    
+
     @Override
     public Reference getReference() throws NamingException {
         final String className = getClass().getName();
-        final String factoryName = className + "Factory"; // XXX: not robust 
+        final String factoryName = className + "Factory"; // XXX: not robust
         Reference ref = new Reference(className, factoryName, null);
         ref.add(new StringRefAddr("instanceKey", instanceKey));
         return ref;

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java Wed Jul 24 21:58:57 2013
@@ -55,6 +55,7 @@ class KeyedCPDSConnectionFactory
     private final String _validationQuery;
     private final boolean _rollbackAfterValidation;
     private KeyedObjectPool<UserPassKey,PooledConnectionAndInfo> _pool;
+    private long maxConnLifetimeMillis = -1;
 
     /**
      * Map of PooledConnections for which close events are ignored.
@@ -165,6 +166,11 @@ class KeyedCPDSConnectionFactory
     @Override
     public boolean validateObject(UserPassKey key,
             PooledObject<PooledConnectionAndInfo> p) {
+        try {
+            validateLifetime(p);
+        } catch (Exception e) {
+            return false;
+        }
         boolean valid = false;
         PooledConnection pconn = p.getObject().getPooledConnection();
         String query = _validationQuery;
@@ -204,11 +210,15 @@ class KeyedCPDSConnectionFactory
     }
 
     @Override
-    public void passivateObject(UserPassKey key, PooledObject<PooledConnectionAndInfo> p) {
+    public void passivateObject(UserPassKey key,
+            PooledObject<PooledConnectionAndInfo> p) throws Exception {
+        validateLifetime(p);
     }
 
     @Override
-    public void activateObject(UserPassKey key, PooledObject<PooledConnectionAndInfo> p) {
+    public void activateObject(UserPassKey key,
+            PooledObject<PooledConnectionAndInfo> p) throws Exception {
+        validateLifetime(p);
     }
 
     // ***********************************************************************
@@ -309,6 +319,16 @@ class KeyedCPDSConnectionFactory
     }
 
     /**
+     * Sets the maximum lifetime in milliseconds of a connection after which the
+     * connection will always fail activation, passivation and validation. A
+     * value of zero or less indicates an infinite lifetime. The default value
+     * is -1.
+     */
+    public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
+    /**
      * This implementation does not fully close the KeyedObjectPool, as
      * this would affect all users.  Instead, it clears the pool associated
      * with the given user.  This method is not currently used.
@@ -322,4 +342,16 @@ class KeyedCPDSConnectionFactory
         }
     }
 
+    private void validateLifetime(PooledObject<PooledConnectionAndInfo> p)
+            throws Exception {
+        if (maxConnLifetimeMillis > 0) {
+            long lifetime = System.currentTimeMillis() - p.getCreateTime();
+            if (lifetime > maxConnLifetimeMillis) {
+                throw new Exception(Utils.getMessage(
+                        "connectionFactory.lifetimeExceeded",
+                        Long.valueOf(lifetime),
+                        Long.valueOf(maxConnLifetimeMillis)));
+            }
+        }
+    }
 }

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java Wed Jul 24 21:58:57 2013
@@ -519,6 +519,7 @@ public class PerUserPoolDataSource exten
         CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds,
                 getValidationQuery(), isRollbackAfterValidation(), username,
                 password);
+        factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
 
         // Create an object pool to contain our PooledConnections
         GenericObjectPool<PooledConnectionAndInfo> pool =

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java Wed Jul 24 21:58:57 2013
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You 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.
@@ -35,11 +35,11 @@ import org.apache.commons.pool2.impl.Gen
 /**
  * <p>A pooling <code>DataSource</code> appropriate for deployment within
  * J2EE environment.  There are many configuration options, most of which are
- * defined in the parent class. All users (based on username) share a single 
+ * defined in the parent class. All users (based on username) share a single
  * maximum number of Connections in this datasource.</p>
- * 
+ *
  * <p>User passwords can be changed without re-initializing the datasource.
- * When a <code>getConnection(username, password)</code> request is processed 
+ * When a <code>getConnection(username, password)</code> request is processed
  * with a password that is different from those used to create connections in the
  * pool associated with <code>username</code>, an attempt is made to create a
  * new connection using the supplied password and if this succeeds, idle connections
@@ -120,7 +120,7 @@ public class SharedPoolDataSource
     /**
      * The maximum number of milliseconds that the pool will wait (when there
      * are no available connections) for a connection to be returned before
-     * throwing an exception, or -1 to wait indefinitely.  Will fail 
+     * throwing an exception, or -1 to wait indefinitely.  Will fail
      * immediately if value is 0.
      * The default is -1.
      */
@@ -131,7 +131,7 @@ public class SharedPoolDataSource
     /**
      * The maximum number of milliseconds that the pool will wait (when there
      * are no available connections) for a connection to be returned before
-     * throwing an exception, or -1 to wait indefinitely.  Will fail 
+     * throwing an exception, or -1 to wait indefinitely.  Will fail
      * immediately if value is 0.
      * The default is -1.
      */
@@ -161,10 +161,10 @@ public class SharedPoolDataSource
     // Inherited abstract methods
 
     @Override
-    protected PooledConnectionAndInfo 
+    protected PooledConnectionAndInfo
         getPooledConnectionAndInfo(String username, String password)
         throws SQLException {
-        
+
         synchronized(this) {
             if (pool == null) {
                 try {
@@ -176,9 +176,9 @@ public class SharedPoolDataSource
         }
 
         PooledConnectionAndInfo info = null;
-        
+
         UserPassKey key = new UserPassKey(username, password);
-        
+
         try {
             info = pool.borrowObject(key);
         }
@@ -188,7 +188,7 @@ public class SharedPoolDataSource
         }
         return info;
     }
-    
+
     @Override
     protected PooledConnectionManager getConnectionManager(UserPassKey upkey)  {
         return factory;
@@ -196,7 +196,7 @@ public class SharedPoolDataSource
 
     /**
      * Returns a <code>SharedPoolDataSource</code> {@link Reference}.
-     * 
+     *
      * @since 1.2.2
      */
     @Override
@@ -206,9 +206,9 @@ public class SharedPoolDataSource
         ref.add(new StringRefAddr("instanceKey", instanceKey));
         return ref;
     }
-    
+
     private void registerPool(
-        String username, String password) 
+        String username, String password)
         throws javax.naming.NamingException, SQLException {
 
         ConnectionPoolDataSource cpds = testCPDS(username, password);
@@ -216,6 +216,7 @@ public class SharedPoolDataSource
         // Create an object pool to contain our PooledConnections
         factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(),
                 isRollbackAfterValidation());
+        factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
         GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
         config.setMaxTotalPerKey(getMaxTotal());
         config.setMaxIdlePerKey(getMaxIdle());
@@ -268,7 +269,7 @@ public class SharedPoolDataSource
      */
     private void readObject(ObjectInputStream in)
         throws IOException, ClassNotFoundException {
-        try 
+        try
         {
             in.defaultReadObject();
             SharedPoolDataSource oldDS = (SharedPoolDataSource)

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/managed/BasicManagedDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/managed/BasicManagedDataSource.java?rev=1506741&r1=1506740&r2=1506741&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/managed/BasicManagedDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp2/managed/BasicManagedDataSource.java Wed Jul 24 21:58:57 2013
@@ -61,7 +61,7 @@ public class BasicManagedDataSource exte
 
     /**
      * Gets the XADataSource instance used by the XAConnectionFactory.
-     * 
+     *
      * @return the XADataSource
      */
     public synchronized XADataSource getXaDataSourceInstance() {
@@ -75,7 +75,7 @@ public class BasicManagedDataSource exte
      * initialized.  The pool is initialized the first time one of the
      * following methods is invoked: <code>getConnection, setLogwriter,
      * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
-     * 
+     *
      * @param xaDataSourceInstance XADataSource instance
      */
     public synchronized void setXaDataSourceInstance(XADataSource xaDataSourceInstance) {
@@ -90,7 +90,7 @@ public class BasicManagedDataSource exte
     public TransactionManager getTransactionManager() {
         return transactionManager;
     }
-    
+
     /**
      * Gets the transaction registry.
      * @return the transaction registry associating XAResources with managed connections
@@ -146,7 +146,7 @@ public class BasicManagedDataSource exte
                 String message = "Cannot load XA data source class '" + xaDataSource + "'";
                 throw (SQLException)new SQLException(message).initCause(t);
             }
-            
+
             try {
                 xaDataSourceInstance = (XADataSource) xaDataSourceClass.newInstance();
             } catch (Exception t) {
@@ -166,9 +166,9 @@ public class BasicManagedDataSource exte
         PoolingDataSource pds = new ManagedDataSource(connectionPool, transactionRegistry);
         pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
         pds.setLogWriter(logWriter);
-        dataSource = pds; 
+        dataSource = pds;
     }
-    
+
     /**
      * Creates the PoolableConnectionFactory and attaches it to the connection pool.
      *
@@ -195,6 +195,7 @@ public class BasicManagedDataSource exte
             connectionFactory.setPoolStatements(poolPreparedStatements);
             connectionFactory.setMaxOpenPrepatedStatements(
                     maxOpenPreparedStatements);
+            connectionFactory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
             validateConnectionFactory(connectionFactory);
         } catch (RuntimeException e) {
             throw e;