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