13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.jndi.ldap.pool;
27
28 import java.util.Map;
29 import java.util.WeakHashMap;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.Set;
34 import java.util.LinkedList;
35
36 import java.io.PrintStream;
37 import java.lang.ref.Reference;
38 import java.lang.ref.ReferenceQueue;
39 import javax.naming.NamingException;
40
41 /**
42 * A map of pool ids to Connections.
43 * Key is an object that uniquely identifies a PooledConnection request
44 * (typically information needed to create the connection).
45 * The definitions of the key's equals() and hashCode() methods are
46 * vital to its unique identification in a Pool.
47 *
48 * Value is a ConnectionsRef, which is a reference to Connections,
49 * a list of equivalent connections.
50 *
51 * Supports methods that
52 * - retrieves (or creates as necessary) a connection from the pool
53 * - removes expired connections from the pool
66 * clearing the weak references is made by the GC it puts the corresponding
67 * ConnectionsWeakRef object into the reference queue.
68 * The reference queue is monitored lazily for reclaimable Connections
69 * whenever a pooled connection is requested or a call to remove the expired
70 * connections is made. The monitoring is done regularly when idle connection
71 * timeout is set as the PoolCleaner removes expired connections periodically.
72 * As determined by the experiements, cleanup of resources using the
73 * ReferenceQueue mechanism is reliable and has immidiate effect than the
74 * finalizer approach.
75 *
76 * @author Rosanna Lee
77 */
78
79 final public class Pool {
80
81 static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug;
82
83 /*
84 * Used for connections cleanup
85 */
86 private static final ReferenceQueue queue = new ReferenceQueue();
87 private static final Collection weakRefs =
88 Collections.synchronizedList(new LinkedList());
89
90 final private int maxSize; // max num of identical conn per pool
91 final private int prefSize; // preferred num of identical conn per pool
92 final private int initSize; // initial number of identical conn to create
93 final private Map map;
94
95 public Pool(int initSize, int prefSize, int maxSize) {
96 map = new WeakHashMap();
97 this.prefSize = prefSize;
98 this.maxSize = maxSize;
99 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 */
118 PooledConnectionFactory factory) throws NamingException {
119
120 d("get(): ", id);
121 d("size: ", map.size());
122
123 expungeStaleConnections();
124
125 Connections conns;
126 synchronized (map) {
127 conns = getConnections(id);
128 if (conns == null) {
129 d("get(): creating new connections list for ", id);
130
131 // No connections for this id so create a new list
132 conns = new Connections(id, initSize, prefSize, maxSize,
133 factory);
134 ConnectionsRef connsRef = new ConnectionsRef(conns);
135 map.put(id, connsRef);
136
137 // Create a weak reference to ConnectionsRef
138 Reference weakRef = new ConnectionsWeakRef(connsRef, queue);
139
140 // Keep the weak reference through the element of a linked list
141 weakRefs.add(weakRef);
142 }
143 }
144
145 d("get(): size after: ", map.size());
146
147 return conns.get(timeout, factory); // get one connection from list
148 }
149
150 private Connections getConnections(Object id) {
151 ConnectionsRef ref = (ConnectionsRef) map.get(id);
152 return (ref != null) ? ref.getConnections() : null;
153 }
154
155 /**
156 * Goes through the connections in this Pool and expires ones that
157 * have been idle before 'threshold'. An expired connection is closed
158 * and then removed from the pool (removePooledConnection() will eventually
159 * be called, and the list of pools itself removed if it becomes empty).
160 *
161 * @param threshold connections idle before 'threshold' should be closed
162 * and removed.
163 */
164 public void expire(long threshold) {
165 synchronized (map) {
166 Collection coll = map.values();
167 Iterator iter = coll.iterator();
168 Connections conns;
169 while (iter.hasNext()) {
170 conns = ((ConnectionsRef) (iter.next())).getConnections();
171 if (conns.expire(threshold)) {
172 d("expire(): removing ", conns);
173 iter.remove();
174 }
175 }
176 }
177 expungeStaleConnections();
178 }
179
180 /*
181 * Closes the connections contained in the ConnectionsRef object that
182 * is going to be reclaimed by the GC. Called by getPooledConnection()
183 * and expire() methods of this class.
184 */
185 private static void expungeStaleConnections() {
186 ConnectionsWeakRef releaseRef = null;
187 while ((releaseRef = (ConnectionsWeakRef) queue.poll())
188 != null) {
189 Connections conns = releaseRef.getConnections();
190
191 if (debug) {
192 System.err.println(
193 "weak reference cleanup: Closing Connections:" + conns);
194 }
195
196 // cleanup
197 conns.close();
198 weakRefs.remove(releaseRef);
199 releaseRef.clear();
200 }
201 }
202
203
204 public void showStats(PrintStream out) {
205 Map.Entry entry;
206 Object id;
207 Connections conns;
208
209 out.println("===== Pool start ======================");
210 out.println("maximum pool size: " + maxSize);
211 out.println("preferred pool size: " + prefSize);
212 out.println("initial pool size: " + initSize);
213 out.println("current pool size: " + map.size());
214
215 Set entries = map.entrySet();
216 Iterator iter = entries.iterator();
217
218 while (iter.hasNext()) {
219 entry = (Map.Entry) iter.next();
220 id = entry.getKey();
221 conns = ((ConnectionsRef) entry.getValue()).getConnections();
222 out.println(" " + id + ":" + conns.getStats());
223 }
224
225 out.println("====== Pool end =====================");
226 }
227
228 public String toString() {
229 return super.toString() + " " + map.toString();
230 }
231
232 private void d(String msg, int i) {
233 if (debug) {
234 System.err.println(this + "." + msg + i);
235 }
236 }
237
238 private void d(String msg, Object obj) {
239 if (debug) {
240 System.err.println(this + "." + msg + obj);
241 }
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.jndi.ldap.pool;
27
28 import java.util.Map;
29 import java.util.WeakHashMap;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.LinkedList;
34
35 import java.io.PrintStream;
36 import java.lang.ref.Reference;
37 import java.lang.ref.ReferenceQueue;
38 import javax.naming.NamingException;
39
40 /**
41 * A map of pool ids to Connections.
42 * Key is an object that uniquely identifies a PooledConnection request
43 * (typically information needed to create the connection).
44 * The definitions of the key's equals() and hashCode() methods are
45 * vital to its unique identification in a Pool.
46 *
47 * Value is a ConnectionsRef, which is a reference to Connections,
48 * a list of equivalent connections.
49 *
50 * Supports methods that
51 * - retrieves (or creates as necessary) a connection from the pool
52 * - removes expired connections from the pool
65 * clearing the weak references is made by the GC it puts the corresponding
66 * ConnectionsWeakRef object into the reference queue.
67 * The reference queue is monitored lazily for reclaimable Connections
68 * whenever a pooled connection is requested or a call to remove the expired
69 * connections is made. The monitoring is done regularly when idle connection
70 * timeout is set as the PoolCleaner removes expired connections periodically.
71 * As determined by the experiements, cleanup of resources using the
72 * ReferenceQueue mechanism is reliable and has immidiate effect than the
73 * finalizer approach.
74 *
75 * @author Rosanna Lee
76 */
77
78 final public class Pool {
79
80 static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug;
81
82 /*
83 * Used for connections cleanup
84 */
85 private static final ReferenceQueue<ConnectionsRef> queue =
86 new ReferenceQueue<>();
87 private static final Collection<Reference<ConnectionsRef>> weakRefs =
88 Collections.synchronizedList(new LinkedList<Reference<ConnectionsRef>>());
89
90 final private int maxSize; // max num of identical conn per pool
91 final private int prefSize; // preferred num of identical conn per pool
92 final private int initSize; // initial number of identical conn to create
93 final private Map<Object, ConnectionsRef> map;
94
95 public Pool(int initSize, int prefSize, int maxSize) {
96 map = new WeakHashMap<>();
97 this.prefSize = prefSize;
98 this.maxSize = maxSize;
99 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 */
118 PooledConnectionFactory factory) throws NamingException {
119
120 d("get(): ", id);
121 d("size: ", map.size());
122
123 expungeStaleConnections();
124
125 Connections conns;
126 synchronized (map) {
127 conns = getConnections(id);
128 if (conns == null) {
129 d("get(): creating new connections list for ", id);
130
131 // No connections for this id so create a new list
132 conns = new Connections(id, initSize, prefSize, maxSize,
133 factory);
134 ConnectionsRef connsRef = new ConnectionsRef(conns);
135 map.put(id, connsRef);
136
137 // Create a weak reference to ConnectionsRef
138 Reference<ConnectionsRef> weakRef =
139 new ConnectionsWeakRef(connsRef, queue);
140
141 // Keep the weak reference through the element of a linked list
142 weakRefs.add(weakRef);
143 }
144 }
145
146 d("get(): size after: ", map.size());
147
148 return conns.get(timeout, factory); // get one connection from list
149 }
150
151 private Connections getConnections(Object id) {
152 ConnectionsRef ref = map.get(id);
153 return (ref != null) ? ref.getConnections() : null;
154 }
155
156 /**
157 * Goes through the connections in this Pool and expires ones that
158 * have been idle before 'threshold'. An expired connection is closed
159 * and then removed from the pool (removePooledConnection() will eventually
160 * be called, and the list of pools itself removed if it becomes empty).
161 *
162 * @param threshold connections idle before 'threshold' should be closed
163 * and removed.
164 */
165 public void expire(long threshold) {
166 synchronized (map) {
167 Iterator<ConnectionsRef> iter = map.values().iterator();
168 Connections conns;
169 while (iter.hasNext()) {
170 conns = iter.next().getConnections();
171 if (conns.expire(threshold)) {
172 d("expire(): removing ", conns);
173 iter.remove();
174 }
175 }
176 }
177 expungeStaleConnections();
178 }
179
180 /*
181 * Closes the connections contained in the ConnectionsRef object that
182 * is going to be reclaimed by the GC. Called by getPooledConnection()
183 * and expire() methods of this class.
184 */
185 private static void expungeStaleConnections() {
186 ConnectionsWeakRef releaseRef = null;
187 while ((releaseRef = (ConnectionsWeakRef) queue.poll())
188 != null) {
189 Connections conns = releaseRef.getConnections();
190
191 if (debug) {
192 System.err.println(
193 "weak reference cleanup: Closing Connections:" + conns);
194 }
195
196 // cleanup
197 conns.close();
198 weakRefs.remove(releaseRef);
199 releaseRef.clear();
200 }
201 }
202
203
204 public void showStats(PrintStream out) {
205 Object id;
206 Connections conns;
207
208 out.println("===== Pool start ======================");
209 out.println("maximum pool size: " + maxSize);
210 out.println("preferred pool size: " + prefSize);
211 out.println("initial pool size: " + initSize);
212 out.println("current pool size: " + map.size());
213
214 for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) {
215 id = entry.getKey();
216 conns = entry.getValue().getConnections();
217 out.println(" " + id + ":" + conns.getStats());
218 }
219
220 out.println("====== Pool end =====================");
221 }
222
223 public String toString() {
224 return super.toString() + " " + map.toString();
225 }
226
227 private void d(String msg, int i) {
228 if (debug) {
229 System.err.println(this + "." + msg + i);
230 }
231 }
232
233 private void d(String msg, Object obj) {
234 if (debug) {
235 System.err.println(this + "." + msg + obj);
236 }
|