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.ArrayList; 29 import java.util.Map; 30 import java.util.WeakHashMap; 31 import java.util.Collection; 32 import java.util.Collections; 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 the experiements, cleanup of resources using the 72 * ReferenceQueue mechanism is reliable and has immidiate 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 Pool(int initSize, int prefSize, int maxSize)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 */ getPooledConnection(Object id, long timeout, PooledConnectionFactory factory)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 getConnections(Object id)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 */ expire(long threshold)168 public void expire(long threshold) { 169 Collection<ConnectionsRef> copy; 170 synchronized (map) { 171 copy = new ArrayList<>(map.values()); 172 } 173 174 ArrayList<ConnectionsRef> removed = new ArrayList<>(); 175 Connections conns; 176 for (ConnectionsRef ref : copy) { 177 conns = ref.getConnections(); 178 if (conns.expire(threshold)) { 179 d("expire(): removing ", conns); 180 removed.add(ref); 181 } 182 } 183 184 synchronized (map) { 185 map.values().removeAll(removed); 186 } 187 188 expungeStaleConnections(); 189 } 190 191 /* 192 * Closes the connections contained in the ConnectionsRef object that 193 * is going to be reclaimed by the GC. Called by getPooledConnection() 194 * and expire() methods of this class. 195 */ expungeStaleConnections()196 private static void expungeStaleConnections() { 197 ConnectionsWeakRef releaseRef = null; 198 while ((releaseRef = (ConnectionsWeakRef) queue.poll()) 199 != null) { 200 Connections conns = releaseRef.getConnections(); 201 202 if (debug) { 203 System.err.println( 204 "weak reference cleanup: Closing Connections:" + conns); 205 } 206 207 // cleanup 208 conns.close(); 209 weakRefs.remove(releaseRef); 210 releaseRef.clear(); 211 } 212 } 213 214 showStats(PrintStream out)215 public void showStats(PrintStream out) { 216 Object id; 217 Connections conns; 218 219 out.println("===== Pool start ======================"); 220 out.println("maximum pool size: " + maxSize); 221 out.println("preferred pool size: " + prefSize); 222 out.println("initial pool size: " + initSize); 223 224 synchronized (map) { 225 out.println("current pool size: " + map.size()); 226 227 for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) { 228 id = entry.getKey(); 229 conns = entry.getValue().getConnections(); 230 out.println(" " + id + ":" + conns.getStats()); 231 } 232 } 233 234 out.println("====== Pool end ====================="); 235 } 236 toString()237 public String toString() { 238 synchronized (map) { 239 return super.toString() + " " + map.toString(); 240 } 241 } 242 d(String msg, int i)243 private void d(String msg, int i) { 244 if (debug) { 245 System.err.println(this + "." + msg + i); 246 } 247 } 248 d(String msg, Object obj)249 private void d(String msg, Object obj) { 250 if (debug) { 251 System.err.println(this + "." + msg + obj); 252 } 253 } 254 } 255