This is an automated email from the ASF dual-hosted git repository. psteitz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-dbcp.git
The following commit(s) were added to refs/heads/master by this push: new 4dd9df42 Make BDS actually implement usage tracking. JIRA: DBCP-590. 4dd9df42 is described below commit 4dd9df42ffe0905c6fd74515017a22505ae863cf Author: Phil Steitz <phil.ste...@gmail.com> AuthorDate: Thu Feb 15 16:06:54 2024 -0700 Make BDS actually implement usage tracking. JIRA: DBCP-590. --- src/changes/changes.xml | 1 + .../org/apache/commons/dbcp2/BasicDataSource.java | 4 +++ .../apache/commons/dbcp2/PoolableConnection.java | 12 +++++++ .../commons/dbcp2/PoolableConnectionFactory.java | 4 ++- .../dbcp2/TestAbandonedBasicDataSource.java | 39 ++++++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 020237e7..481b515b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -64,6 +64,7 @@ The <action> type attribute can be add,update,fix,remove. <body> <release version="2.11.1" date="2024-MM-DD" description="This is a minor release, including bug fixes and enhancements."> <!-- FIX --> + <action type="fix" issue="DBCP-590" dev="psteitz" due-to=" Réda Housni Alaoui">BasicDataSource#setAbandonedUsageTracking has no effect.</action> <action type="fix" issue="DBCP-596" dev="ggregory" due-to="Aapo Haapanen, Gary Gregory">PoolingConnection.toString() causes StackOverflowError.</action> <!-- ADD --> <action type="add" dev="ggregory" due-to="Gary Gregory">Add property project.build.outputTimestamp for build reproducibility.</action> diff --git a/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java b/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java index 1fb8dac8..48b2fedb 100644 --- a/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java +++ b/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java @@ -483,6 +483,10 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean updateJmxName(config); // Disable JMX on the underlying pool if the DS is not registered: config.setJmxEnabled(registeredJmxObjectName != null); + // Set up usage tracking if enabled + if (getAbandonedUsageTracking() && abandonedConfig != null) { + abandonedConfig.setUseUsageTracking(true); + } final GenericObjectPool<PoolableConnection> gop = createObjectPool(factory, config, abandonedConfig); gop.setMaxTotal(maxTotal); gop.setMaxIdle(maxIdle); diff --git a/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java b/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java index 9bc4059a..c99e789a 100644 --- a/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java +++ b/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java @@ -34,6 +34,7 @@ import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPool; /** * A delegating connection that, rather than closing the underlying connection, returns itself to an {@link ObjectPool} @@ -333,6 +334,17 @@ public class PoolableConnection extends DelegatingConnection<Connection> impleme super.closeInternal(); } + @Override + public void setLastUsed() { + super.setLastUsed(); + if (pool instanceof GenericObjectPool<?>) { + final GenericObjectPool<PoolableConnection> gop = (GenericObjectPool<PoolableConnection>) pool; + if (gop.isAbandonedConfig()) { + gop.use(this); + } + } + } + /** * Validates the connection, using the following algorithm: * <ol> diff --git a/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java index 297b4cba..51600251 100644 --- a/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java +++ b/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java @@ -467,7 +467,9 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName, disconnectionSqlCodes, fastFailValidation); pc.setCacheState(cacheState); - return new DefaultPooledObject<>(pc); + final DefaultPooledObject<PoolableConnection> pooledObject = new DefaultPooledObject<>(pc); + pooledObject.setRequireFullStackTrace(true); + return pooledObject; } @Override diff --git a/src/test/java/org/apache/commons/dbcp2/TestAbandonedBasicDataSource.java b/src/test/java/org/apache/commons/dbcp2/TestAbandonedBasicDataSource.java index 8b1d9287..dbd2b929 100644 --- a/src/test/java/org/apache/commons/dbcp2/TestAbandonedBasicDataSource.java +++ b/src/test/java/org/apache/commons/dbcp2/TestAbandonedBasicDataSource.java @@ -389,4 +389,43 @@ public class TestAbandonedBasicDataSource extends TestBasicDataSource { checkLastUsedStatement(st, conn); } } + + @Test + public void testAbandonedStackTraces() throws Exception { + // force abandoned + ds.setRemoveAbandonedTimeout(Duration.ZERO); + ds.setMaxTotal(1); + ds.setAccessToUnderlyingConnectionAllowed(true); + ds.setAbandonedUsageTracking(true); + + try (Connection conn1 = getConnection()) { + assertNotNull(conn1); + assertEquals(1, ds.getNumActive()); + // Use the connection + try (Statement stmt = conn1.createStatement()) { + assertNotNull(stmt); + stmt.execute("SELECT 1 FROM DUAL"); + } + + try (Connection conn2 = getConnection()) { + // Attempt to borrow object triggers abandoned cleanup + // conn1 should be closed by the pool to make room + assertNotNull(conn2); + assertEquals(1, ds.getNumActive()); + // Verify that conn1 is closed + assertTrue(((DelegatingConnection<?>) conn1).getInnermostDelegate().isClosed()); + // Verify that conn1 is aborted + final TesterConnection tCon = (TesterConnection) ((DelegatingConnection<?>) conn1) + .getInnermostDelegate(); + assertTrue(tCon.isAborted()); + + } + assertEquals(0, ds.getNumActive()); + } + assertEquals(0, ds.getNumActive()); + final String stackTrace = sw.toString(); + assertTrue(stackTrace.contains("testAbandonedStackTraces"), stackTrace); + assertTrue(stackTrace.contains("Pooled object created"), stackTrace); + assertTrue(stackTrace.contains("The last code to use this object was:"), stackTrace); + } }