| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.jndi.ldap.pool;  | 
 | 
 | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.WeakHashMap;  | 
 | 
import java.util.Collection;  | 
 | 
import java.util.Collections;  | 
 | 
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;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final ReferenceQueue<ConnectionsRef> queue =  | 
 | 
        new ReferenceQueue<>();  | 
 | 
    private static final Collection<Reference<ConnectionsRef>> weakRefs =  | 
 | 
        Collections.synchronizedList(new LinkedList<Reference<ConnectionsRef>>());  | 
 | 
 | 
 | 
    final private int maxSize;      | 
 | 
    final private int prefSize;     | 
 | 
    final private int initSize;     | 
 | 
    final private Map<Object, ConnectionsRef> map;  | 
 | 
 | 
 | 
    public Pool(int initSize, int prefSize, int maxSize) { | 
 | 
        map = new WeakHashMap<>();  | 
 | 
        this.prefSize = prefSize;  | 
 | 
        this.maxSize = maxSize;  | 
 | 
        this.initSize = initSize;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public PooledConnection getPooledConnection(Object id, long timeout,  | 
 | 
        PooledConnectionFactory factory) throws NamingException { | 
 | 
 | 
 | 
        d("get(): ", id); | 
 | 
        if (debug) { | 
 | 
            synchronized (map) { | 
 | 
                d("size: ", map.size()); | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        expungeStaleConnections();  | 
 | 
 | 
 | 
        Connections conns;  | 
 | 
        synchronized (map) { | 
 | 
            conns = getConnections(id);  | 
 | 
            if (conns == null) { | 
 | 
                d("get(): creating new connections list for ", id); | 
 | 
 | 
 | 
                  | 
 | 
                conns = new Connections(id, initSize, prefSize, maxSize,  | 
 | 
                    factory);  | 
 | 
                ConnectionsRef connsRef = new ConnectionsRef(conns);  | 
 | 
                map.put(id, connsRef);  | 
 | 
 | 
 | 
                  | 
 | 
                Reference<ConnectionsRef> weakRef =  | 
 | 
                        new ConnectionsWeakRef(connsRef, queue);  | 
 | 
 | 
 | 
                  | 
 | 
                weakRefs.add(weakRef);  | 
 | 
            }  | 
 | 
            d("get(): size after: ", map.size()); | 
 | 
        }  | 
 | 
 | 
 | 
        return conns.get(timeout, factory);   | 
 | 
    }  | 
 | 
 | 
 | 
    private Connections getConnections(Object id) { | 
 | 
        ConnectionsRef ref = map.get(id);  | 
 | 
        return (ref != null) ? ref.getConnections() : null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public void expire(long threshold) { | 
 | 
        Collection<ConnectionsRef> copy;  | 
 | 
        synchronized (map) { | 
 | 
            copy = new ArrayList<>(map.values());  | 
 | 
        }  | 
 | 
 | 
 | 
        ArrayList<ConnectionsRef> removed = new ArrayList<>();  | 
 | 
        Connections conns;  | 
 | 
        for (ConnectionsRef ref : copy) { | 
 | 
            conns = ref.getConnections();  | 
 | 
            if (conns.expire(threshold)) { | 
 | 
                d("expire(): removing ", conns); | 
 | 
                removed.add(ref);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        synchronized (map) { | 
 | 
            map.values().removeAll(removed);  | 
 | 
        }  | 
 | 
 | 
 | 
        expungeStaleConnections();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            conns.close();  | 
 | 
            weakRefs.remove(releaseRef);  | 
 | 
            releaseRef.clear();  | 
 | 
         }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public void showStats(PrintStream out) { | 
 | 
        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); | 
 | 
 | 
 | 
        synchronized (map) { | 
 | 
            out.println("current pool size: " + map.size()); | 
 | 
 | 
 | 
            for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) { | 
 | 
                id = entry.getKey();  | 
 | 
                conns = entry.getValue().getConnections();  | 
 | 
                out.println("   " + id + ":" + conns.getStats()); | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        out.println("====== Pool end ====================="); | 
 | 
    }  | 
 | 
 | 
 | 
    public String toString() { | 
 | 
        synchronized (map) { | 
 | 
            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);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |