| /* |
| * 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); |
| } |
| } |
| } |