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 the experiments, cleanup of resources using the 72 * ReferenceQueue mechanism is reliable and has 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 d("size: ", map.size()); 122 123 expungeStaleConnections(); 124 125 Connections conns; 126 synchronized (map) { 127 conns = getConnections(id); 128 if (conns == null) { 129 d("get(): creating new connections list for ", id); 130 131 // No connections for this id so create a new list 132 conns = new Connections(id, initSize, prefSize, maxSize, 133 factory); 134 ConnectionsRef connsRef = new ConnectionsRef(conns); 135 map.put(id, connsRef); 136 137 // Create a weak reference to ConnectionsRef 138 Reference<ConnectionsRef> weakRef = 139 new ConnectionsWeakRef(connsRef, queue); 140 141 // Keep the weak reference through the element of a linked list 142 weakRefs.add(weakRef); 143 } 144 } 145 146 d("get(): size after: ", map.size()); 147 148 return conns.get(timeout, factory); // get one connection from list 149 } 150 151 private Connections getConnections(Object id) { 152 ConnectionsRef ref = map.get(id); 153 return (ref != null) ? ref.getConnections() : null; 154 } 155 156 /** 157 * Goes through the connections in this Pool and expires ones that 158 * have been idle before 'threshold'. An expired connection is closed 159 * and then removed from the pool (removePooledConnection() will eventually 160 * be called, and the list of pools itself removed if it becomes empty). 161 * 162 * @param threshold connections idle before 'threshold' should be closed 163 * and removed. 164 */ 165 public void expire(long threshold) { 166 synchronized (map) { 167 Iterator<ConnectionsRef> iter = map.values().iterator(); 168 Connections conns; 169 while (iter.hasNext()) { 170 conns = iter.next().getConnections(); 171 if (conns.expire(threshold)) { 172 d("expire(): removing ", conns); 173 iter.remove(); 174 } 175 } 176 } 177 expungeStaleConnections(); 178 } 179 180 /* 181 * Closes the connections contained in the ConnectionsRef object that 182 * is going to be reclaimed by the GC. Called by getPooledConnection() 183 * and expire() methods of this class. 184 */ 185 private static void expungeStaleConnections() { 186 ConnectionsWeakRef releaseRef = null; 187 while ((releaseRef = (ConnectionsWeakRef) queue.poll()) 188 != null) { 189 Connections conns = releaseRef.getConnections(); 190 191 if (debug) { 192 System.err.println( 193 "weak reference cleanup: Closing Connections:" + conns); 194 } 195 196 // cleanup 197 conns.close(); 198 weakRefs.remove(releaseRef); 199 releaseRef.clear(); 200 } 201 } 202 203 204 public void showStats(PrintStream out) { 205 Object id; 206 Connections conns; 207 208 out.println("===== Pool start ======================"); 209 out.println("maximum pool size: " + maxSize); 210 out.println("preferred pool size: " + prefSize); 211 out.println("initial pool size: " + initSize); 212 out.println("current pool size: " + map.size()); 213 214 for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) { 215 id = entry.getKey(); 216 conns = entry.getValue().getConnections(); 217 out.println(" " + id + ":" + conns.getStats()); 218 } 219 220 out.println("====== Pool end ====================="); 221 } 222 223 public String toString() { 224 return super.toString() + " " + map.toString(); 225 } 226 227 private void d(String msg, int i) { 228 if (debug) { 229 System.err.println(this + "." + msg + i); 230 } 231 } 232 233 private void d(String msg, Object obj) { 234 if (debug) { 235 System.err.println(this + "." + msg + obj); 236 } 237 } 238 }