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