From 115ed204ea54d5ffd5f54739fb9d1260172b85d7 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 20 Sep 2013 16:06:38 +0000
Subject: [PATCH] Backport fix for OPENDJ-1152 - Provide the ability to debug leaked pooled connections
---
opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 48 insertions(+), 6 deletions(-)
diff --git a/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java b/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
index c2176e9..86d41c1 100644
--- a/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
+++ b/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/CachedConnectionPool.java
@@ -27,8 +27,11 @@
package org.forgerock.opendj.ldap;
+import static com.forgerock.opendj.util.StaticUtils.DEBUG_ENABLED;
import static com.forgerock.opendj.util.StaticUtils.DEBUG_LOG;
import static com.forgerock.opendj.util.StaticUtils.DEFAULT_SCHEDULER;
+import static com.forgerock.opendj.util.StaticUtils.getStackTraceIfDebugEnabled;
+import static com.forgerock.opendj.util.StaticUtils.logIfDebugEnabled;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_CONNECTION_POOL_CLOSING;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
@@ -124,7 +127,7 @@
* the client application closes this connection. More specifically, pooled
* connections are not actually stored in the internal queue.
*/
- private final class PooledConnection implements Connection, ConnectionEventListener {
+ class PooledConnection implements Connection, ConnectionEventListener {
private final Connection connection;
private ErrorResultException error = null;
private final AtomicBoolean isClosed = new AtomicBoolean(false);
@@ -132,7 +135,7 @@
private List<ConnectionEventListener> listeners = null;
private final Object stateLock = new Object();
- private PooledConnection(final Connection connection) {
+ PooledConnection(final Connection connection) {
this.connection = connection;
}
@@ -582,6 +585,24 @@
}
}
+ private final class DebugEnabledPooledConnection extends PooledConnection {
+ private final StackTraceElement[] stackTrace;
+
+ private DebugEnabledPooledConnection(final Connection connection,
+ final StackTraceElement[] stackTrace) {
+ super(connection);
+ this.stackTrace = stackTrace;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (!isClosed()) {
+ logIfDebugEnabled("CONNECTION POOL: connection leaked! It was allocated here: ",
+ stackTrace);
+ }
+ }
+ }
+
/**
* A queue element is either a pending connection request future awaiting an
* {@code Connection} or it is an unused {@code Connection} awaiting a
@@ -590,17 +611,21 @@
private static final class QueueElement {
private final long timestampMillis;
private final Object value;
+ private final StackTraceElement[] stack;
QueueElement(final Connection connection, final long timestampMillis) {
this.value = connection;
this.timestampMillis = timestampMillis;
+ this.stack = null;
}
- QueueElement(final ResultHandler<? super Connection> handler, final long timestampMillis) {
+ QueueElement(final ResultHandler<? super Connection> handler, final long timestampMillis,
+ final StackTraceElement[] stack) {
this.value =
new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(
handler);
this.timestampMillis = timestampMillis;
+ this.stack = stack;
}
@Override
@@ -608,6 +633,10 @@
return String.valueOf(value);
}
+ StackTraceElement[] getStackTrace() {
+ return stack;
+ }
+
Connection getWaitingConnection() {
if (value instanceof Connection) {
return (Connection) value;
@@ -740,7 +769,9 @@
} else if (hasWaitingConnections()) {
holder = queue.removeFirst();
} else {
- holder = new QueueElement(handler, timeSource.currentTimeMillis());
+ holder =
+ new QueueElement(handler, timeSource.currentTimeMillis(),
+ getStackTraceIfDebugEnabled());
queue.add(holder);
}
}
@@ -749,7 +780,8 @@
// There was a completed connection attempt.
final Connection connection = holder.getWaitingConnection();
if (connection.isValid()) {
- final PooledConnection pooledConnection = new PooledConnection(connection);
+ final PooledConnection pooledConnection =
+ newPooledConnection(connection, getStackTraceIfDebugEnabled());
if (handler != null) {
handler.handleResult(pooledConnection);
}
@@ -854,7 +886,17 @@
}
}
} else {
- holder.getWaitingFuture().handleResult(new PooledConnection(connection));
+ holder.getWaitingFuture().handleResult(
+ newPooledConnection(connection, holder.getStackTrace()));
+ }
+ }
+
+ private PooledConnection newPooledConnection(final Connection connection,
+ final StackTraceElement[] stack) {
+ if (!DEBUG_ENABLED) {
+ return new PooledConnection(connection);
+ } else {
+ return new DebugEnabledPooledConnection(connection, stack);
}
}
--
Gitblit v1.10.0