243 lines · 9.0 KB · Java
RawDownload
/* * Copyright (c) 2002, 2003, 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.Map;import java.util.WeakHashMap;import java.util.Collection;import java.util.Collections;import java.util.Iterator;import java.util.Set;import java.util.LinkedList; import java.io.PrintStream;import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import javax.naming.NamingException; /** * A map of pool ids to Connections. * Key is an object that uniquely identifies a PooledConnection request * (typically information needed to create the connection). * The definitions of the key's equals() and hashCode() methods are * vital to its unique identification in a Pool. * * Value is a ConnectionsRef, which is a reference to Connections, * a list of equivalent connections. * * Supports methods that * - retrieves (or creates as necessary) a connection from the pool * - removes expired connections from the pool * * Connections cleanup: * A WeakHashMap is used for mapping the pool ids and Connections. * A SoftReference from the value to the key is kept to hold the map * entry as long as possible. This allows the GC to remove Connections * from the Pool under situations of VM running out of resources. * To take an appropriate action of 'closing the connections' before the GC * reclaims the ConnectionsRef objects, the ConnectionsRef objects are made * weakly reachable through a list of weak references registered with * a reference queue. * Upon an entry gets removed from the WeakHashMap, the ConnectionsRef (value * in the map) object is weakly reachable. When another sweep of * clearing the weak references is made by the GC it puts the corresponding * ConnectionsWeakRef object into the reference queue. * The reference queue is monitored lazily for reclaimable Connections * whenever a pooled connection is requested or a call to remove the expired * connections is made. The monitoring is done regularly when idle connection * timeout is set as the PoolCleaner removes expired connections periodically. * As determined by the experiements, cleanup of resources using the * ReferenceQueue mechanism is reliable and has immidiate effect than the * finalizer approach. * * @author Rosanna Lee */ final public class Pool {     static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug;     /*     * Used for connections cleanup     */    private static final ReferenceQueue queue = new ReferenceQueue();    private static final Collection weakRefs =                Collections.synchronizedList(new LinkedList());     final private int maxSize;    // max num of identical conn per pool    final private int prefSize;   // preferred num of identical conn per pool    final private int initSize;   // initial number of identical conn to create    final private Map map;     public Pool(int initSize, int prefSize, int maxSize) {        map = new WeakHashMap();        this.prefSize = prefSize;        this.maxSize = maxSize;        this.initSize = initSize;    }     /**     * Gets a pooled connection for id. The pooled connection might be     * newly created, as governed by the maxSize and prefSize settings.     * If a pooled connection is unavailable and cannot be created due     * to the maxSize constraint, this call blocks until the constraint     * is removed or until 'timeout' ms has elapsed.     *     * @param id identity of the connection to get     * @param timeout the number of milliseconds to wait before giving up     * @param factory the factory to use for creating the connection if     *          creation is necessary     * @return a pooled connection     * @throws NamingException the connection could not be created due to     *                          an error.     */    public PooledConnection getPooledConnection(Object id, long timeout,        PooledConnectionFactory factory) throws NamingException {         d("get(): ", id);        d("size: ", map.size());         expungeStaleConnections();         Connections conns;        synchronized (map) {            conns = getConnections(id);            if (conns == null) {                d("get(): creating new connections list for ", id);                 // No connections for this id so create a new list                conns = new Connections(id, initSize, prefSize, maxSize,                    factory);                ConnectionsRef connsRef = new ConnectionsRef(conns);                map.put(id, connsRef);                 // Create a weak reference to ConnectionsRef                Reference weakRef = new ConnectionsWeakRef(connsRef, queue);                 // Keep the weak reference through the element of a linked list                weakRefs.add(weakRef);            }        }         d("get(): size after: ", map.size());         return conns.get(timeout, factory); // get one connection from list    }     private Connections getConnections(Object id) {        ConnectionsRef ref = (ConnectionsRef) map.get(id);        return (ref != null) ? ref.getConnections() : null;    }     /**     * Goes through the connections in this Pool and expires ones that     * have been idle before 'threshold'. An expired connection is closed     * and then removed from the pool (removePooledConnection() will eventually     * be called, and the list of pools itself removed if it becomes empty).     *     * @param threshold connections idle before 'threshold' should be closed     *          and removed.     */    public void expire(long threshold) {        synchronized (map) {            Collection coll = map.values();            Iterator iter = coll.iterator();            Connections conns;            while (iter.hasNext()) {                conns = ((ConnectionsRef) (iter.next())).getConnections();                if (conns.expire(threshold)) {                    d("expire(): removing ", conns);                    iter.remove();                }            }        }        expungeStaleConnections();    }     /*     * Closes the connections contained in the ConnectionsRef object that     * is going to be reclaimed by the GC. Called by getPooledConnection()     * and expire() methods of this class.     */    private static void expungeStaleConnections() {        ConnectionsWeakRef releaseRef = null;        while ((releaseRef = (ConnectionsWeakRef) queue.poll())                                        != null) {            Connections conns = releaseRef.getConnections();             if (debug) {                System.err.println(                        "weak reference cleanup: Closing Connections:" + conns);            }             // cleanup            conns.close();            weakRefs.remove(releaseRef);            releaseRef.clear();         }    }      public void showStats(PrintStream out) {        Map.Entry entry;        Object id;        Connections conns;         out.println("===== Pool start ======================");        out.println("maximum pool size: " + maxSize);        out.println("preferred pool size: " + prefSize);        out.println("initial pool size: " + initSize);        out.println("current pool size: " + map.size());         Set entries = map.entrySet();        Iterator iter = entries.iterator();         while (iter.hasNext()) {            entry = (Map.Entry) iter.next();            id = entry.getKey();            conns = ((ConnectionsRef) entry.getValue()).getConnections();            out.println("   " + id + ":" + conns.getStats());        }         out.println("====== Pool end =====================");    }     public String toString() {        return super.toString() + " " + map.toString();    }     private void d(String msg, int i) {        if (debug) {            System.err.println(this + "." + msg + i);        }    }     private void d(String msg, Object obj) {        if (debug) {            System.err.println(this + "." + msg + obj);        }    }}