1 /* 2 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap.pool; 27 28 import java.util.Map; 29 import java.util.WeakHashMap; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.Iterator; 33 import java.util.LinkedList; 34 35 import java.io.PrintStream; 36 import java.lang.ref.Reference; 37 import java.lang.ref.ReferenceQueue; 38 import javax.naming.NamingException; 39 40 /** 41 * A map of pool ids to Connections. 42 * Key is an object that uniquely identifies a PooledConnection request 43 * (typically information needed to create the connection). 44 * The definitions of the key's equals() and hashCode() methods are 45 * vital to its unique identification in a Pool. 46 * 47 * Value is a ConnectionsRef, which is a reference to Connections, 48 * a list of equivalent connections. 49 * 50 * Supports methods that 51 * - retrieves (or creates as necessary) a connection from the pool 52 * - removes expired connections from the pool 53 * 54 * Connections cleanup: 55 * A WeakHashMap is used for mapping the pool ids and Connections. 56 * A SoftReference from the value to the key is kept to hold the map 57 * entry as long as possible. This allows the GC to remove Connections 58 * from the Pool under situations of VM running out of resources. 59 * To take an appropriate action of 'closing the connections' before the GC 60 * reclaims the ConnectionsRef objects, the ConnectionsRef objects are made 61 * weakly reachable through a list of weak references registered with 62 * a reference queue. 63 * Upon an entry gets removed from the WeakHashMap, the ConnectionsRef (value 64 * in the map) object is weakly reachable. When another sweep of 65 * clearing the weak references is made by the GC it puts the corresponding 66 * ConnectionsWeakRef object into the reference queue. 67 * The reference queue is monitored lazily for reclaimable Connections 68 * whenever a pooled connection is requested or a call to remove the expired 69 * connections is made. The monitoring is done regularly when idle connection 70 * timeout is set as the PoolCleaner removes expired connections periodically. 71 * As determined by experimentation, cleanup of resources using the 72 * ReferenceQueue mechanism is reliable and has more immediate effect than the 73 * finalizer approach. 74 * 75 * @author Rosanna Lee 76 */ 77 78 final public class Pool { 79 80 static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug; 81 82 /* 83 * Used for connections cleanup 84 */ 85 private static final ReferenceQueue<ConnectionsRef> queue = 86 new ReferenceQueue<>(); 87 private static final Collection<Reference<ConnectionsRef>> weakRefs = 88 Collections.synchronizedList(new LinkedList<Reference<ConnectionsRef>>()); 89 90 final private int maxSize; // max num of identical conn per pool 91 final private int prefSize; // preferred num of identical conn per pool 92 final private int initSize; // initial number of identical conn to create 93 final private Map<Object, ConnectionsRef> map; 94 95 public Pool(int initSize, int prefSize, int maxSize) { 96 map = new WeakHashMap<>(); 97 this.prefSize = prefSize; 98 this.maxSize = maxSize; 99 this.initSize = initSize; 100 } 101 102 /** 103 * Gets a pooled connection for id. The pooled connection might be 104 * newly created, as governed by the maxSize and prefSize settings. 105 * If a pooled connection is unavailable and cannot be created due 106 * to the maxSize constraint, this call blocks until the constraint 107 * is removed or until 'timeout' ms has elapsed. 108 * 109 * @param id identity of the connection to get 110 * @param timeout the number of milliseconds to wait before giving up 111 * @param factory the factory to use for creating the connection if 112 * creation is necessary 113 * @return a pooled connection 114 * @throws NamingException the connection could not be created due to 115 * an error. 116 */ 117 public PooledConnection getPooledConnection(Object id, long timeout, 118 PooledConnectionFactory factory) throws NamingException { 119 120 d("get(): ", id); 121 if (debug) { 122 synchronized (map) { 123 d("size: ", map.size()); 124 } 125 } 126 127 expungeStaleConnections(); 128 129 Connections conns; 130 synchronized (map) { 131 conns = getConnections(id); 132 if (conns == null) { 133 d("get(): creating new connections list for ", id); 134 135 // No connections for this id so create a new list 136 conns = new Connections(id, initSize, prefSize, maxSize, 137 factory); 138 ConnectionsRef connsRef = new ConnectionsRef(conns); 139 map.put(id, connsRef); 140 141 // Create a weak reference to ConnectionsRef 142 Reference<ConnectionsRef> weakRef = 143 new ConnectionsWeakRef(connsRef, queue); 144 145 // Keep the weak reference through the element of a linked list 146 weakRefs.add(weakRef); 147 } 148 d("get(): size after: ", map.size()); 149 } 150 151 return conns.get(timeout, factory); // get one connection from list 152 } 153 154 private Connections getConnections(Object id) { 155 ConnectionsRef ref = map.get(id); 156 return (ref != null) ? ref.getConnections() : null; 157 } 158 159 /** 160 * Goes through the connections in this Pool and expires ones that 161 * have been idle before 'threshold'. An expired connection is closed 162 * and then removed from the pool (removePooledConnection() will eventually 163 * be called, and the list of pools itself removed if it becomes empty). 164 * 165 * @param threshold connections idle before 'threshold' should be closed 166 * and removed. 167 */ 168 public void expire(long threshold) { 169 synchronized (map) { 170 Iterator<ConnectionsRef> iter = map.values().iterator(); 171 Connections conns; 172 while (iter.hasNext()) { 173 conns = iter.next().getConnections(); 174 if (conns.expire(threshold)) { 175 d("expire(): removing ", conns); 176 iter.remove(); 177 } 178 } 179 } 180 expungeStaleConnections(); 181 } 182 183 /* 184 * Closes the connections contained in the ConnectionsRef object that 185 * is going to be reclaimed by the GC. Called by getPooledConnection() 186 * and expire() methods of this class. 187 */ 188 private static void expungeStaleConnections() { 189 ConnectionsWeakRef releaseRef = null; 190 while ((releaseRef = (ConnectionsWeakRef) queue.poll()) 191 != null) { 192 Connections conns = releaseRef.getConnections(); 193 194 if (debug) { 195 System.err.println( 196 "weak reference cleanup: Closing Connections:" + conns); 197 } 198 199 // cleanup 200 conns.close(); 201 weakRefs.remove(releaseRef); 202 releaseRef.clear(); 203 } 204 } 205 206 207 public void showStats(PrintStream out) { 208 Object id; 209 Connections conns; 210 211 out.println("===== Pool start ======================"); 212 out.println("maximum pool size: " + maxSize); 213 out.println("preferred pool size: " + prefSize); 214 out.println("initial pool size: " + initSize); 215 216 synchronized (map) { 217 out.println("current pool size: " + map.size()); 218 219 for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) { 220 id = entry.getKey(); 221 conns = entry.getValue().getConnections(); 222 out.println(" " + id + ":" + conns.getStats()); 223 } 224 } 225 226 out.println("====== Pool end ====================="); 227 } 228 229 public String toString() { 230 synchronized (map) { 231 return super.toString() + " " + map.toString(); 232 } 233 } 234 235 private void d(String msg, int i) { 236 if (debug) { 237 System.err.println(this + "." + msg + i); 238 } 239 } 240 241 private void d(String msg, Object obj) { 242 if (debug) { 243 System.err.println(this + "." + msg + obj); 244 } 245 } 246 }