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; // JDK 1.2 29 import java.util.List; 30 import java.util.Iterator; 31 32 import java.lang.ref.Reference; 33 import java.lang.ref.SoftReference; 34 35 import javax.naming.NamingException; 36 import javax.naming.InterruptedNamingException; 37 import javax.naming.CommunicationException; 38 39 /** 40 * Represents a list of PooledConnections (actually, ConnectionDescs) with the 41 * same pool id. 42 * The list starts out with an initial number of connections. 43 * Additional PooledConnections are created lazily upon demand. 44 * The list has a maximum size. When the number of connections 45 * reaches the maximum size, a request for a PooledConnection blocks until 46 * a connection is returned to the list. A maximum size of zero means that 47 * there is no maximum: connection creation will be attempted when 48 * no idle connection is available. 49 * 50 * The list may also have a preferred size. If the current list size 51 * is less than the preferred size, a request for a connection will result in 52 * a PooledConnection being created (even if an idle connection is available). 53 * If the current list size is greater than the preferred size, 54 * a connection being returned to the list will be closed and removed from 55 * the list. A preferred size of zero means that there is no preferred size: 56 * connections are created only when no idle connection is available and 57 * a connection being returned to the list is not closed. Regardless of the 58 * preferred size, connection creation always observes the maximum size: 59 * a connection won't be created if the list size is at or exceeds the 60 * maximum size. 61 * 62 * @author Rosanna Lee 63 */ 64 65 // Package private: accessed only by Pool 66 final class Connections implements PoolCallback { 67 private static final boolean debug = Pool.debug; 68 private static final boolean trace = 69 com.sun.jndi.ldap.LdapPoolManager.trace; 70 private static final int DEFAULT_SIZE = 10; 71 72 final private int maxSize; 73 final private int prefSize; 74 final private List<ConnectionDesc> conns; 75 76 private boolean closed = false; // Closed for business 77 private Reference<Object> ref; // maintains reference to id to prevent premature GC 78 79 /** 80 * @param id the identity (connection request) of the connections in the list 81 * @param initSize the number of connections to create initially 82 * @param prefSize the preferred size of the pool. The pool will try 83 * to maintain a pool of this size by creating and closing connections 84 * as needed. 85 * @param maxSize the maximum size of the pool. The pool will not exceed 86 * this size. If the pool is at this size, a request for a connection 87 * will block until an idle connection is released to the pool or 88 * when one is removed. 89 * @param factory The factory responsible for creating a connection 90 */ 91 Connections(Object id, int initSize, int prefSize, int maxSize, 92 PooledConnectionFactory factory) throws NamingException { 93 94 this.maxSize = maxSize; 95 if (maxSize > 0) { 96 // prefSize and initSize cannot exceed specified maxSize 97 this.prefSize = Math.min(prefSize, maxSize); 98 initSize = Math.min(initSize, maxSize); 99 } else { 100 this.prefSize = prefSize; 101 } 102 conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE); 103 104 // Maintain soft ref to id so that this Connections' entry in 105 // Pool doesn't get GC'ed prematurely 106 ref = new SoftReference<>(id); 107 108 d("init size=", initSize); 109 d("max size=", maxSize); 110 d("preferred size=", prefSize); 111 112 // Create initial connections 113 PooledConnection conn; 114 for (int i = 0; i < initSize; i++) { 115 conn = factory.createPooledConnection(this); 116 td("Create ", conn ,factory); 117 conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool 118 } 119 } 120 121 /** 122 * Retrieves a PooledConnection from this list of connections. 123 * Use an existing one if one is idle, or create one if the list's 124 * max size hasn't been reached. If max size has been reached, wait 125 * for a PooledConnection to be returned, or one to be removed (thus 126 * not reaching the max size any longer). 127 * 128 * @param timeout if > 0, msec to wait until connection is available 129 * @param factory creates the PooledConnection if one needs to be created 130 * 131 * @return A non-null PooledConnection 132 * @throws NamingException PooledConnection cannot be created, because this 133 * thread was interrupted while it waited for an available connection, 134 * or if it timed out while waiting, or the creation of a connection 135 * resulted in an error. 136 */ 137 synchronized PooledConnection get(long timeout, 138 PooledConnectionFactory factory) throws NamingException { 139 PooledConnection conn; 140 long start = (timeout > 0 ? System.currentTimeMillis() : 0); 141 long waittime = timeout; 142 143 d("get(): before"); 144 while ((conn = getOrCreateConnection(factory)) == null) { 145 if (timeout > 0 && waittime <= 0) { 146 throw new CommunicationException( 147 "Timeout exceeded while waiting for a connection: " + 148 timeout + "ms"); 149 } 150 try { 151 d("get(): waiting"); 152 if (waittime > 0) { 153 wait(waittime); // Wait until one is released or removed 154 } else { 155 wait(); 156 } 157 } catch (InterruptedException e) { 158 throw new InterruptedNamingException( 159 "Interrupted while waiting for a connection"); 160 } 161 // Check whether we timed out 162 if (timeout > 0) { 163 long now = System.currentTimeMillis(); 164 waittime = timeout - (now - start); 165 } 166 } 167 168 d("get(): after"); 169 return conn; 170 } 171 172 /** 173 * Retrieves an idle connection from this list if one is available. 174 * If none is available, create a new one if maxSize hasn't been reached. 175 * If maxSize has been reached, return null. 176 * Always called from a synchronized method. 177 */ 178 private PooledConnection getOrCreateConnection( 179 PooledConnectionFactory factory) throws NamingException { 180 181 int size = conns.size(); // Current number of idle/nonidle conns 182 PooledConnection conn = null; 183 184 if (prefSize <= 0 || size >= prefSize) { 185 // If no prefSize specified, or list size already meets or 186 // exceeds prefSize, then first look for an idle connection 187 ConnectionDesc entry; 188 for (int i = 0; i < size; i++) { 189 entry = conns.get(i); 190 if ((conn = entry.tryUse()) != null) { 191 d("get(): use ", conn); 192 td("Use ", conn); 193 return conn; 194 } 195 } 196 } 197 198 // Check if list size already at maxSize specified 199 if (maxSize > 0 && size >= maxSize) { 200 return null; // List size is at limit; cannot create any more 201 } 202 203 conn = factory.createPooledConnection(this); 204 td("Create and use ", conn, factory); 205 conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool 206 207 return conn; 208 } 209 210 /** 211 * Releases connection back into list. 212 * If the list size is below prefSize, the connection may be reused. 213 * If the list size exceeds prefSize, then the connection is closed 214 * and removed from the list. 215 * 216 * public because implemented as part of PoolCallback. 217 */ 218 public synchronized boolean releasePooledConnection(PooledConnection conn) { 219 ConnectionDesc entry; 220 int loc = conns.indexOf(entry=new ConnectionDesc(conn)); 221 222 d("release(): ", conn); 223 224 if (loc >= 0) { 225 // Found entry 226 227 if (closed || (prefSize > 0 && conns.size() > prefSize)) { 228 // If list size exceeds prefSize, close connection 229 230 d("release(): closing ", conn); 231 td("Close ", conn); 232 233 // size must be >= 2 so don't worry about empty list 234 conns.remove(entry); 235 conn.closeConnection(); 236 237 } else { 238 d("release(): release ", conn); 239 td("Release ", conn); 240 241 // Get ConnectionDesc from list to get correct state info 242 entry = conns.get(loc); 243 // Return connection to list, ready for reuse 244 entry.release(); 245 } 246 notifyAll(); 247 d("release(): notify"); 248 return true; 249 } else { 250 return false; 251 } 252 } 253 254 /** 255 * Removes PooledConnection from list of connections. 256 * The closing of the connection is separate from this method. 257 * This method is called usually when the caller encounters an error 258 * when using the connection and wants it removed from the pool. 259 * 260 * @return true if conn removed; false if it was not in pool 261 * 262 * public because implemented as part of PoolCallback. 263 */ 264 public synchronized boolean removePooledConnection(PooledConnection conn) { 265 if (conns.remove(new ConnectionDesc(conn))) { 266 d("remove(): ", conn); 267 268 notifyAll(); 269 270 d("remove(): notify"); 271 td("Remove ", conn); 272 273 if (conns.isEmpty()) { 274 // Remove softref to make pool entry eligible for GC. 275 // Once ref has been removed, it cannot be reinstated. 276 ref = null; 277 } 278 279 return true; 280 } else { 281 d("remove(): not found ", conn); 282 return false; 283 } 284 } 285 286 /** 287 * Goes through all entries in list, removes and closes ones that have been 288 * idle before threshold. 289 * 290 * @param threshold an entry idle since this time has expired. 291 * @return true if no more connections in list 292 */ 293 synchronized boolean expire(long threshold) { 294 Iterator<ConnectionDesc> iter = conns.iterator(); 295 ConnectionDesc entry; 296 while (iter.hasNext()) { 297 entry = iter.next(); 298 if (entry.expire(threshold)) { 299 d("expire(): removing ", entry); 300 td("Expired ", entry); 301 302 iter.remove(); // remove from pool 303 304 // Don't need to call notify() because we're 305 // removing only idle connections. If there were 306 // idle connections, then there should be no waiters. 307 } 308 } 309 return conns.isEmpty(); // whether whole list has 'expired' 310 } 311 312 /** 313 * Called when this instance of Connections has been removed from Pool. 314 * This means that no one can get any pooled connections from this 315 * Connections any longer. Expire all idle connections as of 'now' 316 * and leave indicator so that any in-use connections will be closed upon 317 * their return. 318 */ 319 synchronized void close() { 320 expire(System.currentTimeMillis()); // Expire idle connections 321 closed = true; // Close in-use connections when they are returned 322 } 323 324 String getStats() { 325 int idle = 0; 326 int busy = 0; 327 int expired = 0; 328 long use = 0; 329 int len; 330 331 synchronized (this) { 332 len = conns.size(); 333 334 ConnectionDesc entry; 335 for (int i = 0; i < len; i++) { 336 entry = conns.get(i); 337 use += entry.getUseCount(); 338 switch (entry.getState()) { 339 case ConnectionDesc.BUSY: 340 ++busy; 341 break; 342 case ConnectionDesc.IDLE: 343 ++idle; 344 break; 345 case ConnectionDesc.EXPIRED: 346 ++expired; 347 } 348 } 349 } 350 return "size=" + len + "; use=" + use + "; busy=" + busy 351 + "; idle=" + idle + "; expired=" + expired; 352 } 353 354 private void d(String msg, Object o1) { 355 if (debug) { 356 d(msg + o1); 357 } 358 } 359 360 private void d(String msg, int i) { 361 if (debug) { 362 d(msg + i); 363 } 364 } 365 366 private void d(String msg) { 367 if (debug) { 368 System.err.println(this + "." + msg + "; size: " + conns.size()); 369 } 370 } 371 372 private void td(String msg, Object o1, Object o2) { 373 if (trace) { // redo test to avoid object creation 374 td(msg + o1 + "[" + o2 + "]"); 375 } 376 } 377 private void td(String msg, Object o1) { 378 if (trace) { // redo test to avoid object creation 379 td(msg + o1); 380 } 381 } 382 private void td(String msg) { 383 if (trace) { 384 System.err.println(msg); 385 } 386 } 387 }