From 82d2f17448db2c1e569802e4ab1ba35db30ac1fb Mon Sep 17 00:00:00 2001
From: fhanik
removeAbandonedTimeout has been reached.
+ (long) Time in milliseconds to keep this connection. When a connection is returned to the pool,
+ the pool will check to see if the now - time-when-connected > maxAge has been reached,
+ and if so, it closes the connection rather than returning it to the pool.
+ The default value is 0, which implies that connections will be left open and no age check
+ will be done upon returning the connection to the pool.
(boolean) Set to true if you wish the ProxyConnection class to use String.equals instead of
== when comparing method names. This property does not apply to added interceptors as those are configured individually.
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
index f5a39a255..1989d5e88 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
@@ -609,6 +609,17 @@ public class ConnectionPool {
}
}
+ protected boolean shouldClose(PooledConnection con, int action) {
+ if (con.isDiscarded()) return true;
+ if (isClosed()) return true;
+ if (!con.validate(action)) return true;
+ if (getPoolProperties().getMaxAge()>0 ) {
+ return (System.currentTimeMillis()-con.getLastConnected()) > getPoolProperties().getMaxAge();
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns a connection to the pool
* @param con PooledConnection
@@ -626,8 +637,8 @@ public class ConnectionPool {
con.lock();
if (busy.remove(con)) {
- if ((!con.isDiscarded()) && (!isClosed()) &&
- con.validate(PooledConnection.VALIDATE_RETURN)) {
+
+ if (!shouldClose(con,PooledConnection.VALIDATE_RETURN)) {
con.setStackTrace(null);
con.setTimestamp(System.currentTimeMillis());
if (((idle.size()>=poolProperties.getMaxIdle()) && !poolProperties.isPoolSweeperEnabled()) || (!idle.offer(con))) {
@@ -654,12 +665,11 @@ public class ConnectionPool {
} //end if
} //checkIn
- public boolean shouldAbandon() {
+ protected boolean shouldAbandon() {
if (poolProperties.getAbandonWhenPercentageFull()==0) return true;
float used = (float)busy.size();
float max = (float)poolProperties.getMaxActive();
float perc = (float)poolProperties.getAbandonWhenPercentageFull();
- System.out.println("Abandon rate:"+(used/max*100f));
return (used/max*100f)>=perc;
}
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java
index 65eb82ddd..c4cc4af8a 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java
@@ -249,6 +249,14 @@ public class DataSource extends DataSourceProxy implements MBeanRegistration,jav
throw new RuntimeException(x);
}
}
+
+ public long getMaxAge() {
+ try {
+ return createPool().getPoolProperties().getMaxAge();
+ }catch (SQLException x) {
+ throw new RuntimeException(x);
+ }
+ }
public String getName() {
try {
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
index ac09e202c..54e45c858 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
@@ -78,6 +78,7 @@ public class DataSourceFactory implements ObjectFactory {
protected final static String PROP_MINIDLE = "minIdle";
protected final static String PROP_INITIALSIZE = "initialSize";
protected final static String PROP_MAXWAIT = "maxWait";
+ protected final static String PROP_MAXAGE = "maxAge";
protected final static String PROP_TESTONBORROW = "testOnBorrow";
protected final static String PROP_TESTONRETURN = "testOnReturn";
@@ -149,7 +150,8 @@ public class DataSourceFactory implements ObjectFactory {
PROP_FAIR_QUEUE,
PROP_USE_EQUALS,
OBJECT_NAME,
- PROP_ABANDONWHENPERCENTAGEFULL
+ PROP_ABANDONWHENPERCENTAGEFULL,
+ PROP_MAXAGE
};
// -------------------------------------------------- ObjectFactory Methods
@@ -417,6 +419,11 @@ public class DataSourceFactory implements ObjectFactory {
poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value));
}
+ value = properties.getProperty(PROP_MAXAGE);
+ if (value != null) {
+ poolProperties.setMaxAge(Long.parseLong(value));
+ }
+
return poolProperties;
}
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
index 37e01d244..9d47b9b02 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
@@ -71,6 +71,7 @@ public class PoolProperties {
protected boolean fairQueue = true;
protected boolean useEquals = false;
protected int abandonWhenPercentageFull = 0;
+ protected long maxAge = 0;
private InterceptorDefinition[] interceptors = null;
@@ -523,4 +524,14 @@ public class PoolProperties {
this.useEquals = useEquals;
}
+ public long getMaxAge() {
+ return maxAge;
+ }
+
+ public void setMaxAge(long maxAge) {
+ this.maxAge = maxAge;
+ }
+
+
+
}
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
index bc051d342..429e6af1a 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
@@ -86,6 +86,10 @@ public class PooledConnection {
*/
protected boolean discarded = false;
/**
+ * The Timestamp when the last time the connect() method was called successfully
+ */
+ protected volatile long lastConnected = -1;
+ /**
* timestamp to keep track of validation intervals
*/
protected long lastValidated = System.currentTimeMillis();
@@ -163,6 +167,7 @@ public class PooledConnection {
if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation());
}
this.discarded = false;
+ this.lastConnected = System.currentTimeMillis();
}
/**
@@ -193,6 +198,7 @@ public class PooledConnection {
}
}
connection = null;
+ lastConnected = -1;
if (finalize) parent.finalize(this);
}
@@ -381,6 +387,12 @@ public class PooledConnection {
public java.sql.Connection getConnection() {
return this.connection;
}
+
+
+
+ public long getLastConnected() {
+ return lastConnected;
+ }
/**
* Returns the first handler in the interceptor chain
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
index cdc18a1fc..577d887fd 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
@@ -276,5 +276,8 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co
public int getAbandonWhenPercentageFull() {
return pool.getPoolProperties().getAbandonWhenPercentageFull();
}
+ public long getMaxAge() {
+ return pool.getPoolProperties().getMaxAge();
+ }
}
diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java
index 1b8711bfa..aab9f24ad 100644
--- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java
+++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java
@@ -118,4 +118,6 @@ public interface ConnectionPoolMBean {
public int getAbandonWhenPercentageFull();
+ public long getMaxAge();
+
}
diff --git a/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java b/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java
new file mode 100644
index 000000000..3f30b6be1
--- /dev/null
+++ b/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.jdbc.test;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer;
+
+public class AbandonPercentageTest extends DefaultTestCase {
+
+ public AbandonPercentageTest(String name) {
+ super(name);
+ }
+
+ public void testDefaultAbandon() throws Exception {
+ this.init();
+ this.datasource.setMaxActive(100);
+ this.datasource.setMaxIdle(100);
+ this.datasource.setInitialSize(0);
+ this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0);
+ this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
+ this.datasource.getPoolProperties().setRemoveAbandoned(true);
+ this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
+ Connection con = datasource.getConnection();
+ long start = System.currentTimeMillis();
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ Thread.sleep(2000);
+ assertEquals("Number of connections active/busy should be 0",0,datasource.getPool().getActive());
+ con.close();
+ }
+
+ public void testMaxedOutAbandon() throws Exception {
+ int size = 100;
+ this.init();
+ this.datasource.setMaxActive(size);
+ this.datasource.setMaxIdle(size);
+ this.datasource.setInitialSize(0);
+ this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100);
+ this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
+ this.datasource.getPoolProperties().setRemoveAbandoned(true);
+ this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
+ Connection con = datasource.getConnection();
+ long start = System.currentTimeMillis();
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ Thread.sleep(2000);
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ con.close();
+ }
+
+ public void testResetConnection() throws Exception {
+ int size = 1;
+ this.init();
+ this.datasource.setMaxActive(size);
+ this.datasource.setMaxIdle(size);
+ this.datasource.setInitialSize(0);
+ this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100);
+ this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100);
+ this.datasource.getPoolProperties().setRemoveAbandoned(true);
+ this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
+ this.datasource.getPoolProperties().setJdbcInterceptors(ResetAbandonedTimer.class.getName());
+ Connection con = datasource.getConnection();
+ long start = System.currentTimeMillis();
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ for (int i=0; i<20; i++) {
+ Thread.sleep(200);
+ con.isClosed();
+ }
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ con.close();
+ }
+
+ public void testHalfway() throws Exception {
+ int size = 100;
+ this.init();
+ this.datasource.setMaxActive(size);
+ this.datasource.setMaxIdle(size);
+ this.datasource.setInitialSize(0);
+ this.datasource.getPoolProperties().setAbandonWhenPercentageFull(50);
+ this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(500);
+ this.datasource.getPoolProperties().setRemoveAbandoned(true);
+ this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1);
+ Connection[] con = new Connection[size];
+ con[0] = datasource.getConnection();
+ long start = System.currentTimeMillis();
+ assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive());
+ for (int i=1; i<25; i++) {
+ con[i] = datasource.getConnection();
+ }
+ assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive());
+ Thread.sleep(2500);
+ assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive());
+ for (int i=25; i