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