001: /*
002: * Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap.pool;
027:
028: import java.util.Map;
029: import java.util.WeakHashMap;
030: import java.util.Collection;
031: import java.util.Collections;
032: import java.util.Iterator;
033: import java.util.Set;
034: import java.util.LinkedList;
035:
036: import java.io.PrintStream;
037: import java.lang.ref.Reference;
038: import java.lang.ref.ReferenceQueue;
039: import javax.naming.NamingException;
040:
041: /**
042: * A map of pool ids to Connections.
043: * Key is an object that uniquely identifies a PooledConnection request
044: * (typically information needed to create the connection).
045: * The definitions of the key's equals() and hashCode() methods are
046: * vital to its unique identification in a Pool.
047: *
048: * Value is a ConnectionsRef, which is a reference to Connections,
049: * a list of equivalent connections.
050: *
051: * Supports methods that
052: * - retrieves (or creates as necessary) a connection from the pool
053: * - removes expired connections from the pool
054: *
055: * Connections cleanup:
056: * A WeakHashMap is used for mapping the pool ids and Connections.
057: * A SoftReference from the value to the key is kept to hold the map
058: * entry as long as possible. This allows the GC to remove Connections
059: * from the Pool under situations of VM running out of resources.
060: * To take an appropriate action of 'closing the connections' before the GC
061: * reclaims the ConnectionsRef objects, the ConnectionsRef objects are made
062: * weakly reachable through a list of weak references registered with
063: * a reference queue.
064: * Upon an entry gets removed from the WeakHashMap, the ConnectionsRef (value
065: * in the map) object is weakly reachable. When another sweep of
066: * clearing the weak references is made by the GC it puts the corresponding
067: * ConnectionsWeakRef object into the reference queue.
068: * The reference queue is monitored lazily for reclaimable Connections
069: * whenever a pooled connection is requested or a call to remove the expired
070: * connections is made. The monitoring is done regularly when idle connection
071: * timeout is set as the PoolCleaner removes expired connections periodically.
072: * As determined by the experiements, cleanup of resources using the
073: * ReferenceQueue mechanism is reliable and has immidiate effect than the
074: * finalizer approach.
075: *
076: * @author Rosanna Lee
077: */
078:
079: final public class Pool {
080:
081: static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug;
082:
083: /*
084: * Used for connections cleanup
085: */
086: private static final ReferenceQueue queue = new ReferenceQueue();
087: private static final Collection weakRefs = Collections
088: .synchronizedList(new LinkedList());
089:
090: final private int maxSize; // max num of identical conn per pool
091: final private int prefSize; // preferred num of identical conn per pool
092: final private int initSize; // initial number of identical conn to create
093: final private Map map;
094:
095: public Pool(int initSize, int prefSize, int maxSize) {
096: map = new WeakHashMap();
097: this .prefSize = prefSize;
098: this .maxSize = maxSize;
099: 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,
118: long timeout, PooledConnectionFactory factory)
119: throws NamingException {
120:
121: d("get(): ", id);
122: d("size: ", map.size());
123:
124: expungeStaleConnections();
125:
126: Connections conns;
127: synchronized (map) {
128: conns = getConnections(id);
129: if (conns == null) {
130: d("get(): creating new connections list for ", id);
131:
132: // No connections for this id so create a new list
133: conns = new Connections(id, initSize, prefSize,
134: maxSize, factory);
135: ConnectionsRef connsRef = new ConnectionsRef(conns);
136: map.put(id, connsRef);
137:
138: // Create a weak reference to ConnectionsRef
139: Reference weakRef = new ConnectionsWeakRef(connsRef,
140: queue);
141:
142: // Keep the weak reference through the element of a linked list
143: weakRefs.add(weakRef);
144: }
145: }
146:
147: d("get(): size after: ", map.size());
148:
149: return conns.get(timeout, factory); // get one connection from list
150: }
151:
152: private Connections getConnections(Object id) {
153: ConnectionsRef ref = (ConnectionsRef) map.get(id);
154: return (ref != null) ? ref.getConnections() : null;
155: }
156:
157: /**
158: * Goes through the connections in this Pool and expires ones that
159: * have been idle before 'threshold'. An expired connection is closed
160: * and then removed from the pool (removePooledConnection() will eventually
161: * be called, and the list of pools itself removed if it becomes empty).
162: *
163: * @param threshold connections idle before 'threshold' should be closed
164: * and removed.
165: */
166: public void expire(long threshold) {
167: synchronized (map) {
168: Collection coll = map.values();
169: Iterator iter = coll.iterator();
170: Connections conns;
171: while (iter.hasNext()) {
172: conns = ((ConnectionsRef) (iter.next()))
173: .getConnections();
174: if (conns.expire(threshold)) {
175: d("expire(): removing ", conns);
176: iter.remove();
177: }
178: }
179: }
180: expungeStaleConnections();
181: }
182:
183: /*
184: * Closes the connections contained in the ConnectionsRef object that
185: * is going to be reclaimed by the GC. Called by getPooledConnection()
186: * and expire() methods of this class.
187: */
188: private static void expungeStaleConnections() {
189: ConnectionsWeakRef releaseRef = null;
190: while ((releaseRef = (ConnectionsWeakRef) queue.poll()) != null) {
191: Connections conns = releaseRef.getConnections();
192:
193: if (debug) {
194: System.err
195: .println("weak reference cleanup: Closing Connections:"
196: + conns);
197: }
198:
199: // cleanup
200: conns.close();
201: weakRefs.remove(releaseRef);
202: releaseRef.clear();
203: }
204: }
205:
206: public void showStats(PrintStream out) {
207: Map.Entry entry;
208: Object id;
209: Connections conns;
210:
211: out.println("===== Pool start ======================");
212: out.println("maximum pool size: " + maxSize);
213: out.println("preferred pool size: " + prefSize);
214: out.println("initial pool size: " + initSize);
215: out.println("current pool size: " + map.size());
216:
217: Set entries = map.entrySet();
218: Iterator iter = entries.iterator();
219:
220: while (iter.hasNext()) {
221: entry = (Map.Entry) iter.next();
222: id = entry.getKey();
223: conns = ((ConnectionsRef) entry.getValue())
224: .getConnections();
225: out.println(" " + id + ":" + conns.getStats());
226: }
227:
228: out.println("====== Pool end =====================");
229: }
230:
231: public String toString() {
232: return super .toString() + " " + map.toString();
233: }
234:
235: private void d(String msg, int i) {
236: if (debug) {
237: System.err.println(this + "." + msg + i);
238: }
239: }
240:
241: private void d(String msg, Object obj) {
242: if (debug) {
243: System.err.println(this + "." + msg + obj);
244: }
245: }
246: }
|