/* * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap.pool; import java.util.ArrayList; // JDK 1.2import java.util.List;import java.util.Iterator; import java.lang.ref.Reference;import java.lang.ref.SoftReference; import javax.naming.NamingException;import javax.naming.InterruptedNamingException;import javax.naming.CommunicationException; /** * Represents a list of PooledConnections (actually, ConnectionDescs) with the * same pool id. * The list starts out with an initial number of connections. * Additional PooledConnections are created lazily upon demand. * The list has a maximum size. When the number of connections * reaches the maximum size, a request for a PooledConnection blocks until * a connection is returned to the list. A maximum size of zero means that * there is no maximum: connection creation will be attempted when * no idle connection is available. * * The list may also have a preferred size. If the current list size * is less than the preferred size, a request for a connection will result in * a PooledConnection being created (even if an idle connection is available). * If the current list size is greater than the preferred size, * a connection being returned to the list will be closed and removed from * the list. A preferred size of zero means that there is no preferred size: * connections are created only when no idle connection is available and * a connection being returned to the list is not closed. Regardless of the * preferred size, connection creation always observes the maximum size: * a connection won't be created if the list size is at or exceeds the * maximum size. * * @author Rosanna Lee */ // Package private: accessed only by Poolfinal class Connections implements PoolCallback { private static final boolean debug = Pool.debug; private static final boolean trace = com.sun.jndi.ldap.LdapPoolManager.trace; private static final int DEFAULT_SIZE = 10; final private int maxSize; final private int prefSize; final private List conns; private boolean closed = false; // Closed for business private Reference ref; // maintains reference to id to prevent premature GC /** * @param id the identity (connection request) of the connections in the list * @param initSize the number of connections to create initially * @param prefSize the preferred size of the pool. The pool will try * to maintain a pool of this size by creating and closing connections * as needed. * @param maxSize the maximum size of the pool. The pool will not exceed * this size. If the pool is at this size, a request for a connection * will block until an idle connection is released to the pool or * when one is removed. * @param factory The factory responsible for creating a connection */ Connections(Object id, int initSize, int prefSize, int maxSize, PooledConnectionFactory factory) throws NamingException { this.maxSize = maxSize; if (maxSize > 0) { // prefSize and initSize cannot exceed specified maxSize this.prefSize = Math.min(prefSize, maxSize); initSize = Math.min(initSize, maxSize); } else { this.prefSize = prefSize; } conns = new ArrayList(maxSize > 0 ? maxSize : DEFAULT_SIZE); // Maintain soft ref to id so that this Connections' entry in // Pool doesn't get GC'ed prematurely ref = new SoftReference(id); d("init size=", initSize); d("max size=", maxSize); d("preferred size=", prefSize); // Create initial connections PooledConnection conn; for (int i = 0; i < initSize; i++) { conn = factory.createPooledConnection(this); td("Create ", conn ,factory); conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool } } /** * Retrieves a PooledConnection from this list of connections. * Use an existing one if one is idle, or create one if the list's * max size hasn't been reached. If max size has been reached, wait * for a PooledConnection to be returned, or one to be removed (thus * not reaching the max size any longer). * * @param timeout if > 0, msec to wait until connection is available * @param factory creates the PooledConnection if one needs to be created * * @return A non-null PooledConnection * @throws NamingException PooledConnection cannot be created, because this * thread was interrupted while it waited for an available connection, * or if it timed out while waiting, or the creation of a connection * resulted in an error. */ synchronized PooledConnection get(long timeout, PooledConnectionFactory factory) throws NamingException { PooledConnection conn; long start = (timeout > 0 ? System.currentTimeMillis() : 0); long waittime = timeout; d("get(): before"); while ((conn = getOrCreateConnection(factory)) == null) { if (timeout > 0 && waittime <= 0) { throw new CommunicationException( "Timeout exceeded while waiting for a connection: " + timeout + "ms"); } try { d("get(): waiting"); if (waittime > 0) { wait(waittime); // Wait until one is released or removed } else { wait(); } } catch (InterruptedException e) { throw new InterruptedNamingException( "Interrupted while waiting for a connection"); } // Check whether we timed out if (timeout > 0) { long now = System.currentTimeMillis(); waittime = timeout - (now - start); } } d("get(): after"); return conn; } /** * Retrieves an idle connection from this list if one is available. * If none is available, create a new one if maxSize hasn't been reached. * If maxSize has been reached, return null. * Always called from a synchronized method. */ private PooledConnection getOrCreateConnection( PooledConnectionFactory factory) throws NamingException { int size = conns.size(); // Current number of idle/nonidle conns PooledConnection conn = null; if (prefSize <= 0 || size >= prefSize) { // If no prefSize specified, or list size already meets or // exceeds prefSize, then first look for an idle connection ConnectionDesc entry; for (int i = 0; i < size; i++) { entry = (ConnectionDesc) conns.get(i); if ((conn = entry.tryUse()) != null) { d("get(): use ", conn); td("Use ", conn); return conn; } } } // Check if list size already at maxSize specified if (maxSize > 0 && size >= maxSize) { return null; // List size is at limit; cannot create any more } conn = factory.createPooledConnection(this); td("Create and use ", conn, factory); conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool return conn; } /** * Releases connection back into list. * If the list size is below prefSize, the connection may be reused. * If the list size exceeds prefSize, then the connection is closed * and removed from the list. * * public because implemented as part of PoolCallback. */ public synchronized boolean releasePooledConnection(PooledConnection conn) { ConnectionDesc entry; int loc = conns.indexOf(entry=new ConnectionDesc(conn)); d("release(): ", conn); if (loc >= 0) { // Found entry if (closed || (prefSize > 0 && conns.size() > prefSize)) { // If list size exceeds prefSize, close connection d("release(): closing ", conn); td("Close ", conn); // size must be >= 2 so don't worry about empty list conns.remove(entry); conn.closeConnection(); } else { d("release(): release ", conn); td("Release ", conn); // Get ConnectionDesc from list to get correct state info entry = (ConnectionDesc) conns.get(loc); // Return connection to list, ready for reuse entry.release(); } notifyAll(); d("release(): notify"); return true; } else { return false; } } /** * Removes PooledConnection from list of connections. * The closing of the connection is separate from this method. * This method is called usually when the caller encouters an error * when using the connection and wants it removed from the pool. * * @return true if conn removed; false if it was not in pool * * public because implemented as part of PoolCallback. */ public synchronized boolean removePooledConnection(PooledConnection conn) { if (conns.remove(new ConnectionDesc(conn))) { d("remove(): ", conn); notifyAll(); d("remove(): notify"); td("Remove ", conn); if (conns.isEmpty()) { // Remove softref to make pool entry eligible for GC. // Once ref has been removed, it cannot be reinstated. ref = null; } return true; } else { d("remove(): not found ", conn); return false; } } /** * Goes through all entries in list, removes and closes ones that have been * idle before threshold. * * @param threshold an entry idle since this time has expired. * @return true if no more connections in list */ synchronized boolean expire(long threshold) { Iterator iter = conns.iterator(); ConnectionDesc entry; while (iter.hasNext()) { entry = (ConnectionDesc) iter.next(); if (entry.expire(threshold)) { d("expire(): removing ", entry); td("Expired ", entry); iter.remove(); // remove from pool // Don't need to call notify() because we're // removing only idle connections. If there were // idle connections, then there should be no waiters. } } return conns.isEmpty(); // whether whole list has 'expired' } /** * Called when this instance of Connections has been removed from Pool. * This means that no one can get any pooled connections from this * Connections any longer. Expire all idle connections as of 'now' * and leave indicator so that any in-use connections will be closed upon * their return. */ synchronized void close() { expire(System.currentTimeMillis()); // Expire idle connections closed = true; // Close in-use connections when they are returned } String getStats() { int idle = 0; int busy = 0; int expired = 0; long use = 0; int len; synchronized (this) { len = conns.size(); ConnectionDesc entry; for (int i = 0; i < len; i++) { entry = (ConnectionDesc) conns.get(i); use += entry.getUseCount(); switch (entry.getState()) { case ConnectionDesc.BUSY: ++busy; break; case ConnectionDesc.IDLE: ++idle; break; case ConnectionDesc.EXPIRED: ++expired; } } } return "size=" + len + "; use=" + use + "; busy=" + busy + "; idle=" + idle + "; expired=" + expired; } private void d(String msg, Object o1) { if (debug) { d(msg + o1); } } private void d(String msg, int i) { if (debug) { d(msg + i); } } private void d(String msg) { if (debug) { System.err.println(this + "." + msg + "; size: " + conns.size()); } } private void td(String msg, Object o1, Object o2) { if (trace) { // redo test to avoid object creation td(msg + o1 + "[" + o2 + "]"); } } private void td(String msg, Object o1) { if (trace) { // redo test to avoid object creation td(msg + o1); } } private void td(String msg) { if (trace) { System.err.println(msg); } }}
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
import java.util.ArrayList; // JDK 1.2
import java.util.List;
import java.util.Iterator;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import javax.naming.NamingException;
import javax.naming.InterruptedNamingException;
import javax.naming.CommunicationException;
/**
* Represents a list of PooledConnections (actually, ConnectionDescs) with the
* same pool id.
* The list starts out with an initial number of connections.
* Additional PooledConnections are created lazily upon demand.
* The list has a maximum size. When the number of connections
* reaches the maximum size, a request for a PooledConnection blocks until
* a connection is returned to the list. A maximum size of zero means that
* there is no maximum: connection creation will be attempted when
* no idle connection is available.
*
* The list may also have a preferred size. If the current list size
* is less than the preferred size, a request for a connection will result in
* a PooledConnection being created (even if an idle connection is available).
* If the current list size is greater than the preferred size,
* a connection being returned to the list will be closed and removed from
* the list. A preferred size of zero means that there is no preferred size:
* connections are created only when no idle connection is available and
* a connection being returned to the list is not closed. Regardless of the
* preferred size, connection creation always observes the maximum size:
* a connection won't be created if the list size is at or exceeds the
* maximum size.
*
* @author Rosanna Lee
*/
// Package private: accessed only by Pool
final class Connections implements PoolCallback {
private static final boolean debug = Pool.debug;
private static final boolean trace =
com.sun.jndi.ldap.LdapPoolManager.trace;
private static final int DEFAULT_SIZE = 10;
final private int maxSize;
final private int prefSize;
final private List conns;
private boolean closed = false; // Closed for business
private Reference ref; // maintains reference to id to prevent premature GC
/**
* @param id the identity (connection request) of the connections in the list
* @param initSize the number of connections to create initially
* @param prefSize the preferred size of the pool. The pool will try
* to maintain a pool of this size by creating and closing connections
* as needed.
* @param maxSize the maximum size of the pool. The pool will not exceed
* this size. If the pool is at this size, a request for a connection
* will block until an idle connection is released to the pool or
* when one is removed.
* @param factory The factory responsible for creating a connection
*/
Connections(Object id, int initSize, int prefSize, int maxSize,
PooledConnectionFactory factory) throws NamingException {
this.maxSize = maxSize;
if (maxSize > 0) {
// prefSize and initSize cannot exceed specified maxSize
this.prefSize = Math.min(prefSize, maxSize);
initSize = Math.min(initSize, maxSize);
} else {
this.prefSize = prefSize;
}
conns = new ArrayList(maxSize > 0 ? maxSize : DEFAULT_SIZE);
// Maintain soft ref to id so that this Connections' entry in
// Pool doesn't get GC'ed prematurely
ref = new SoftReference(id);
d("init size=", initSize);
d("max size=", maxSize);
d("preferred size=", prefSize);
// Create initial connections
PooledConnection conn;
for (int i = 0; i < initSize; i++) {
conn = factory.createPooledConnection(this);
td("Create ", conn ,factory);
conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool
}
}
/**
* Retrieves a PooledConnection from this list of connections.
* Use an existing one if one is idle, or create one if the list's
* max size hasn't been reached. If max size has been reached, wait
* for a PooledConnection to be returned, or one to be removed (thus
* not reaching the max size any longer).
*
* @param timeout if > 0, msec to wait until connection is available
* @param factory creates the PooledConnection if one needs to be created
*
* @return A non-null PooledConnection
* @throws NamingException PooledConnection cannot be created, because this
* thread was interrupted while it waited for an available connection,
* or if it timed out while waiting, or the creation of a connection
* resulted in an error.
*/
synchronized PooledConnection get(long timeout,
PooledConnectionFactory factory) throws NamingException {
PooledConnection conn;
long start = (timeout > 0 ? System.currentTimeMillis() : 0);
long waittime = timeout;
d("get(): before");
while ((conn = getOrCreateConnection(factory)) == null) {
if (timeout > 0 && waittime <= 0) {
throw new CommunicationException(
"Timeout exceeded while waiting for a connection: " +
timeout + "ms");
}
try {
d("get(): waiting");
if (waittime > 0) {
wait(waittime); // Wait until one is released or removed
} else {
wait();
}
} catch (InterruptedException e) {
throw new InterruptedNamingException(
"Interrupted while waiting for a connection");
}
// Check whether we timed out
if (timeout > 0) {
long now = System.currentTimeMillis();
waittime = timeout - (now - start);
}
}
d("get(): after");
return conn;
}
/**
* Retrieves an idle connection from this list if one is available.
* If none is available, create a new one if maxSize hasn't been reached.
* If maxSize has been reached, return null.
* Always called from a synchronized method.
*/
private PooledConnection getOrCreateConnection(
PooledConnectionFactory factory) throws NamingException {
int size = conns.size(); // Current number of idle/nonidle conns
PooledConnection conn = null;
if (prefSize <= 0 || size >= prefSize) {
// If no prefSize specified, or list size already meets or
// exceeds prefSize, then first look for an idle connection
ConnectionDesc entry;
for (int i = 0; i < size; i++) {
entry = (ConnectionDesc) conns.get(i);
if ((conn = entry.tryUse()) != null) {
d("get(): use ", conn);
td("Use ", conn);
return conn;
}
}
}
// Check if list size already at maxSize specified
if (maxSize > 0 && size >= maxSize) {
return null; // List size is at limit; cannot create any more
}
conn = factory.createPooledConnection(this);
td("Create and use ", conn, factory);
conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool
return conn;
}
/**
* Releases connection back into list.
* If the list size is below prefSize, the connection may be reused.
* If the list size exceeds prefSize, then the connection is closed
* and removed from the list.
*
* public because implemented as part of PoolCallback.
*/
public synchronized boolean releasePooledConnection(PooledConnection conn) {
ConnectionDesc entry;
int loc = conns.indexOf(entry=new ConnectionDesc(conn));
d("release(): ", conn);
if (loc >= 0) {
// Found entry
if (closed || (prefSize > 0 && conns.size() > prefSize)) {
// If list size exceeds prefSize, close connection
d("release(): closing ", conn);
td("Close ", conn);
// size must be >= 2 so don't worry about empty list
conns.remove(entry);
conn.closeConnection();
} else {
d("release(): release ", conn);
td("Release ", conn);
// Get ConnectionDesc from list to get correct state info
entry = (ConnectionDesc) conns.get(loc);
// Return connection to list, ready for reuse
entry.release();
}
notifyAll();
d("release(): notify");
return true;
} else {
return false;
}
}
/**
* Removes PooledConnection from list of connections.
* The closing of the connection is separate from this method.
* This method is called usually when the caller encouters an error
* when using the connection and wants it removed from the pool.
*
* @return true if conn removed; false if it was not in pool
*
* public because implemented as part of PoolCallback.
*/
public synchronized boolean removePooledConnection(PooledConnection conn) {
if (conns.remove(new ConnectionDesc(conn))) {
d("remove(): ", conn);
notifyAll();
d("remove(): notify");
td("Remove ", conn);
if (conns.isEmpty()) {
// Remove softref to make pool entry eligible for GC.
// Once ref has been removed, it cannot be reinstated.
ref = null;
}
return true;
} else {
d("remove(): not found ", conn);
return false;
}
}
/**
* Goes through all entries in list, removes and closes ones that have been
* idle before threshold.
*
* @param threshold an entry idle since this time has expired.
* @return true if no more connections in list
*/
synchronized boolean expire(long threshold) {
Iterator iter = conns.iterator();
ConnectionDesc entry;
while (iter.hasNext()) {
entry = (ConnectionDesc) iter.next();
if (entry.expire(threshold)) {
d("expire(): removing ", entry);
td("Expired ", entry);
iter.remove(); // remove from pool
// Don't need to call notify() because we're
// removing only idle connections. If there were
// idle connections, then there should be no waiters.
}
}
return conns.isEmpty(); // whether whole list has 'expired'
}
/**
* Called when this instance of Connections has been removed from Pool.
* This means that no one can get any pooled connections from this
* Connections any longer. Expire all idle connections as of 'now'
* and leave indicator so that any in-use connections will be closed upon
* their return.
*/
synchronized void close() {
expire(System.currentTimeMillis()); // Expire idle connections
closed = true; // Close in-use connections when they are returned
}
String getStats() {
int idle = 0;
int busy = 0;
int expired = 0;
long use = 0;
int len;
synchronized (this) {
len = conns.size();
ConnectionDesc entry;
for (int i = 0; i < len; i++) {
entry = (ConnectionDesc) conns.get(i);
use += entry.getUseCount();
switch (entry.getState()) {
case ConnectionDesc.BUSY:
++busy;
break;
case ConnectionDesc.IDLE:
++idle;
break;
case ConnectionDesc.EXPIRED:
++expired;
}
}
}
return "size=" + len + "; use=" + use + "; busy=" + busy
+ "; idle=" + idle + "; expired=" + expired;
}
private void d(String msg, Object o1) {
if (debug) {
d(msg + o1);
}
}
private void d(String msg, int i) {
if (debug) {
d(msg + i);
}
}
private void d(String msg) {
if (debug) {
System.err.println(this + "." + msg + "; size: " + conns.size());
}
}
private void td(String msg, Object o1, Object o2) {
if (trace) { // redo test to avoid object creation
td(msg + o1 + "[" + o2 + "]");
}
}
private void td(String msg, Object o1) {
if (trace) { // redo test to avoid object creation
td(msg + o1);
}
}
private void td(String msg) {
if (trace) {
System.err.println(msg);
}
}
}