55/**
66 * A buffer designed especially to hold pooled connections (free and busy ones)
77 * <p>
8+ * The buffer contains two linkedLists (free and busy connection nodes)
9+ * <p>
10+ * When a node from the free list is removed, the node is attached to the
11+ * PooledConnection, so that the node object can be reused. This avoids object
12+ * creation/gc during remove operations.
13+ * <p>
814 * All thread safety controlled externally (by PooledConnectionQueue).
915 * </p>
1016 */
1117final class ConnectionBuffer {
1218
13-
1419 private final Node free = Node .init ();
20+ private final Node busy = Node .init ();
1521
1622 int freeSize = 0 ;
23+ int busySize = 0 ;
1724
1825 /**
1926 * Return the number of entries in the buffer.
@@ -22,6 +29,13 @@ int freeSize() {
2229 return freeSize ;
2330 }
2431
32+ /**
33+ * Return the number of busy connections.
34+ */
35+ int busySize () {
36+ return busySize ;
37+ }
38+
2539 /**
2640 * Return true if the buffer is empty.
2741 */
@@ -30,28 +44,74 @@ boolean hasFreeConnections() {
3044 }
3145
3246 /**
33- * Add connection to the free list.
47+ * Adds a new connection to the free list.
3448 */
3549 void addFree (PooledConnection pc ) {
50+ assert pc .busyNode () == null : "Connection seems not to be new" ;
3651 new Node (pc ).addAfter (free );
3752 freeSize ++;
3853 }
3954
55+ /**
56+ * Removes the connection from the busy list. (For full close)
57+ * Returns true, if this connection was part of the busy list or false, if not (or removed twice)
58+ */
59+ boolean removeBusy (PooledConnection c ) {
60+ if (c .busyNode () == null ) {
61+ return false ;
62+ }
63+ c .busyNode ().remove ();
64+ busySize --;
65+ c .setBusyNode (null );
66+ return true ;
67+ }
68+
69+ /**
70+ * Moves the connection from the busy list to the free list.
71+ */
72+ boolean moveToFreeList (PooledConnection c ) {
73+ Node node = c .busyNode ();
74+ if (node == null ) {
75+ return false ;
76+ }
77+ node .remove ();
78+ busySize --;
79+ node .addAfter (free );
80+ freeSize ++;
81+ c .setBusyNode (null );
82+ return true ;
83+ }
84+
4085 /**
4186 * Remove a connection from the free list. Returns <code>null</code> if there is not any.
4287 */
43- PooledConnection popFree () {
88+ PooledConnection popFree () {
4489 Node node = free .next ;
4590 if (node .isBoundaryNode ()) {
4691 return null ;
4792 }
4893 node .remove ();
4994 freeSize --;
95+ node .pc .setBusyNode (node ); // sets the node for reuse in "addBusy"
5096 return node .pc ;
5197 }
5298
5399 /**
54- * Close all connections in the free list.
100+ * Adds the connection to the busy list. The connection must be either new or popped from the free list.
101+ */
102+ int addBusy (PooledConnection c ) {
103+ Node node = c .busyNode (); // we try to reuse the node to avoid object creation.
104+ if (node == null ) {
105+ node = new Node (c );
106+ c .setBusyNode (node );
107+ }
108+ node .addAfter (busy );
109+ busySize ++;
110+ return busySize ;
111+ }
112+
113+ /**
114+ * Close all free connections in this buffer.
55115 */
56116 void closeAllFree (boolean logErrors ) {
57117 List <PooledConnection > tempList = new ArrayList <>();
@@ -92,6 +152,56 @@ int trim(int minSize, long usedSince, long createdSince) {
92152 return trimCount ;
93153 }
94154
155+ void closeBusyConnections (long leakTimeMinutes ) {
156+ long olderThanTime = System .currentTimeMillis () - (leakTimeMinutes * 60000 );
157+ Log .debug ("Closing busy connections using leakTimeMinutes {0}" , leakTimeMinutes );
158+ Node node = busy .next ;
159+ while (!node .isBoundaryNode ()) {
160+ Node current = node ;
161+ node = node .next ;
162+
163+ PooledConnection pc = current .pc ;
164+ //noinspection StatementWithEmptyBody
165+ if (pc .lastUsedTime () > olderThanTime ) {
166+ // PooledConnection has been used recently or
167+ // expected to be longRunning so not closing...
168+ } else {
169+ current .remove ();
170+ --busySize ;
171+ closeBusyConnection (pc );
172+ }
173+ }
174+ }
175+
176+ private void closeBusyConnection (PooledConnection pc ) {
177+ try {
178+ Log .warn ("DataSource closing busy connection? {0}" , pc .fullDescription ());
179+ System .out .println ("CLOSING busy connection: " + pc .fullDescription ());
180+ pc .closeConnectionFully (false );
181+ } catch (Exception ex ) {
182+ Log .error ("Error when closing potentially leaked connection " + pc .description (), ex );
183+ }
184+ }
185+
186+ String busyConnectionInformation (boolean toLogger ) {
187+ if (toLogger ) {
188+ Log .info ("Dumping [{0}] busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)" , busySize ());
189+ }
190+ StringBuilder sb = new StringBuilder ();
191+ Node node = busy .next ;
192+ while (!node .isBoundaryNode ()) {
193+ PooledConnection pc = node .pc ;
194+ node = node .next ;
195+ if (toLogger ) {
196+ Log .info ("Busy Connection - {0}" , pc .fullDescription ());
197+ } else {
198+ sb .append (pc .fullDescription ()).append ("\r \n " );
199+ }
200+ }
201+ return sb .toString ();
202+ }
203+
204+
95205 /**
96206 * Node of a linkedlist. The linkedLists always have two empty nodes at the start and end.
97207 * (boundary nodes) They are generated with the init() method.
0 commit comments