blob: 319ff433e64cabcc96790132afdf8e6f486d49a9 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.jndi.ldap.pool;
27
28import java.util.ArrayList; // JDK 1.2
29import java.util.List;
30import java.util.Iterator;
31
32import java.lang.ref.Reference;
33import java.lang.ref.SoftReference;
34
35import javax.naming.NamingException;
36import javax.naming.InterruptedNamingException;
37import javax.naming.CommunicationException;
38
39/**
40 * Represents a list of PooledConnections (actually, ConnectionDescs) with the
41 * same pool id.
42 * The list starts out with an initial number of connections.
43 * Additional PooledConnections are created lazily upon demand.
44 * The list has a maximum size. When the number of connections
45 * reaches the maximum size, a request for a PooledConnection blocks until
46 * a connection is returned to the list. A maximum size of zero means that
47 * there is no maximum: connection creation will be attempted when
48 * no idle connection is available.
49 *
50 * The list may also have a preferred size. If the current list size
51 * is less than the preferred size, a request for a connection will result in
52 * a PooledConnection being created (even if an idle connection is available).
53 * If the current list size is greater than the preferred size,
54 * a connection being returned to the list will be closed and removed from
55 * the list. A preferred size of zero means that there is no preferred size:
56 * connections are created only when no idle connection is available and
57 * a connection being returned to the list is not closed. Regardless of the
58 * preferred size, connection creation always observes the maximum size:
59 * a connection won't be created if the list size is at or exceeds the
60 * maximum size.
61 *
62 * @author Rosanna Lee
63 */
64
65// Package private: accessed only by Pool
66final class Connections implements PoolCallback {
67 private static final boolean debug = Pool.debug;
68 private static final boolean trace =
69 com.sun.jndi.ldap.LdapPoolManager.trace;
70 private static final int DEFAULT_SIZE = 10;
71
72 final private int maxSize;
73 final private int prefSize;
74 final private List conns;
75
76 private boolean closed = false; // Closed for business
77 private Reference ref; // maintains reference to id to prevent premature GC
78
79 /**
80 * @param id the identity (connection request) of the connections in the list
81 * @param initSize the number of connections to create initially
82 * @param prefSize the preferred size of the pool. The pool will try
83 * to maintain a pool of this size by creating and closing connections
84 * as needed.
85 * @param maxSize the maximum size of the pool. The pool will not exceed
86 * this size. If the pool is at this size, a request for a connection
87 * will block until an idle connection is released to the pool or
88 * when one is removed.
89 * @param factory The factory responsible for creating a connection
90 */
91 Connections(Object id, int initSize, int prefSize, int maxSize,
92 PooledConnectionFactory factory) throws NamingException {
93
94 this.maxSize = maxSize;
95 if (maxSize > 0) {
96 // prefSize and initSize cannot exceed specified maxSize
97 this.prefSize = Math.min(prefSize, maxSize);
98 initSize = Math.min(initSize, maxSize);
99 } else {
100 this.prefSize = prefSize;
101 }
102 conns = new ArrayList(maxSize > 0 ? maxSize : DEFAULT_SIZE);
103
104 // Maintain soft ref to id so that this Connections' entry in
105 // Pool doesn't get GC'ed prematurely
106 ref = new SoftReference(id);
107
108 d("init size=", initSize);
109 d("max size=", maxSize);
110 d("preferred size=", prefSize);
111
112 // Create initial connections
113 PooledConnection conn;
114 for (int i = 0; i < initSize; i++) {
115 conn = factory.createPooledConnection(this);
116 td("Create ", conn ,factory);
117 conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool
118 }
119 }
120
121 /**
122 * Retrieves a PooledConnection from this list of connections.
123 * Use an existing one if one is idle, or create one if the list's
124 * max size hasn't been reached. If max size has been reached, wait
125 * for a PooledConnection to be returned, or one to be removed (thus
126 * not reaching the max size any longer).
127 *
128 * @param timeout if > 0, msec to wait until connection is available
129 * @param factory creates the PooledConnection if one needs to be created
130 *
131 * @return A non-null PooledConnection
132 * @throws NamingException PooledConnection cannot be created, because this
133 * thread was interrupted while it waited for an available connection,
134 * or if it timed out while waiting, or the creation of a connection
135 * resulted in an error.
136 */
137 synchronized PooledConnection get(long timeout,
138 PooledConnectionFactory factory) throws NamingException {
139 PooledConnection conn;
140 long start = (timeout > 0 ? System.currentTimeMillis() : 0);
141 long waittime = timeout;
142
143 d("get(): before");
144 while ((conn = getOrCreateConnection(factory)) == null) {
145 if (timeout > 0 && waittime <= 0) {
146 throw new CommunicationException(
147 "Timeout exceeded while waiting for a connection: " +
148 timeout + "ms");
149 }
150 try {
151 d("get(): waiting");
152 if (waittime > 0) {
153 wait(waittime); // Wait until one is released or removed
154 } else {
155 wait();
156 }
157 } catch (InterruptedException e) {
158 throw new InterruptedNamingException(
159 "Interrupted while waiting for a connection");
160 }
161 // Check whether we timed out
162 if (timeout > 0) {
163 long now = System.currentTimeMillis();
164 waittime = timeout - (now - start);
165 }
166 }
167
168 d("get(): after");
169 return conn;
170 }
171
172 /**
173 * Retrieves an idle connection from this list if one is available.
174 * If none is available, create a new one if maxSize hasn't been reached.
175 * If maxSize has been reached, return null.
176 * Always called from a synchronized method.
177 */
178 private PooledConnection getOrCreateConnection(
179 PooledConnectionFactory factory) throws NamingException {
180
181 int size = conns.size(); // Current number of idle/nonidle conns
182 PooledConnection conn = null;
183
184 if (prefSize <= 0 || size >= prefSize) {
185 // If no prefSize specified, or list size already meets or
186 // exceeds prefSize, then first look for an idle connection
187 ConnectionDesc entry;
188 for (int i = 0; i < size; i++) {
189 entry = (ConnectionDesc) conns.get(i);
190 if ((conn = entry.tryUse()) != null) {
191 d("get(): use ", conn);
192 td("Use ", conn);
193 return conn;
194 }
195 }
196 }
197
198 // Check if list size already at maxSize specified
199 if (maxSize > 0 && size >= maxSize) {
200 return null; // List size is at limit; cannot create any more
201 }
202
203 conn = factory.createPooledConnection(this);
204 td("Create and use ", conn, factory);
205 conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool
206
207 return conn;
208 }
209
210 /**
211 * Releases connection back into list.
212 * If the list size is below prefSize, the connection may be reused.
213 * If the list size exceeds prefSize, then the connection is closed
214 * and removed from the list.
215 *
216 * public because implemented as part of PoolCallback.
217 */
218 public synchronized boolean releasePooledConnection(PooledConnection conn) {
219 ConnectionDesc entry;
220 int loc = conns.indexOf(entry=new ConnectionDesc(conn));
221
222 d("release(): ", conn);
223
224 if (loc >= 0) {
225 // Found entry
226
227 if (closed || (prefSize > 0 && conns.size() > prefSize)) {
228 // If list size exceeds prefSize, close connection
229
230 d("release(): closing ", conn);
231 td("Close ", conn);
232
233 // size must be >= 2 so don't worry about empty list
234 conns.remove(entry);
235 conn.closeConnection();
236
237 } else {
238 d("release(): release ", conn);
239 td("Release ", conn);
240
241 // Get ConnectionDesc from list to get correct state info
242 entry = (ConnectionDesc) conns.get(loc);
243 // Return connection to list, ready for reuse
244 entry.release();
245 }
246 notifyAll();
247 d("release(): notify");
248 return true;
249 } else {
250 return false;
251 }
252 }
253
254 /**
255 * Removes PooledConnection from list of connections.
256 * The closing of the connection is separate from this method.
257 * This method is called usually when the caller encouters an error
258 * when using the connection and wants it removed from the pool.
259 *
260 * @return true if conn removed; false if it was not in pool
261 *
262 * public because implemented as part of PoolCallback.
263 */
264 public synchronized boolean removePooledConnection(PooledConnection conn) {
265 if (conns.remove(new ConnectionDesc(conn))) {
266 d("remove(): ", conn);
267
268 notifyAll();
269
270 d("remove(): notify");
271 td("Remove ", conn);
272
273 if (conns.isEmpty()) {
274 // Remove softref to make pool entry eligible for GC.
275 // Once ref has been removed, it cannot be reinstated.
276 ref = null;
277 }
278
279 return true;
280 } else {
281 d("remove(): not found ", conn);
282 return false;
283 }
284 }
285
286 /**
287 * Goes through all entries in list, removes and closes ones that have been
288 * idle before threshold.
289 *
290 * @param threshold an entry idle since this time has expired.
291 * @return true if no more connections in list
292 */
293 synchronized boolean expire(long threshold) {
294 Iterator iter = conns.iterator();
295 ConnectionDesc entry;
296 while (iter.hasNext()) {
297 entry = (ConnectionDesc) iter.next();
298 if (entry.expire(threshold)) {
299 d("expire(): removing ", entry);
300 td("Expired ", entry);
301
302 iter.remove(); // remove from pool
303
304 // Don't need to call notify() because we're
305 // removing only idle connections. If there were
306 // idle connections, then there should be no waiters.
307 }
308 }
309 return conns.isEmpty(); // whether whole list has 'expired'
310 }
311
312 /**
313 * Called when this instance of Connections has been removed from Pool.
314 * This means that no one can get any pooled connections from this
315 * Connections any longer. Expire all idle connections as of 'now'
316 * and leave indicator so that any in-use connections will be closed upon
317 * their return.
318 */
319 synchronized void close() {
320 expire(System.currentTimeMillis()); // Expire idle connections
321 closed = true; // Close in-use connections when they are returned
322 }
323
324 String getStats() {
325 int idle = 0;
326 int busy = 0;
327 int expired = 0;
328 long use = 0;
329 int len;
330
331 synchronized (this) {
332 len = conns.size();
333
334 ConnectionDesc entry;
335 for (int i = 0; i < len; i++) {
336 entry = (ConnectionDesc) conns.get(i);
337 use += entry.getUseCount();
338 switch (entry.getState()) {
339 case ConnectionDesc.BUSY:
340 ++busy;
341 break;
342 case ConnectionDesc.IDLE:
343 ++idle;
344 break;
345 case ConnectionDesc.EXPIRED:
346 ++expired;
347 }
348 }
349 }
350 return "size=" + len + "; use=" + use + "; busy=" + busy
351 + "; idle=" + idle + "; expired=" + expired;
352 }
353
354 private void d(String msg, Object o1) {
355 if (debug) {
356 d(msg + o1);
357 }
358 }
359
360 private void d(String msg, int i) {
361 if (debug) {
362 d(msg + i);
363 }
364 }
365
366 private void d(String msg) {
367 if (debug) {
368 System.err.println(this + "." + msg + "; size: " + conns.size());
369 }
370 }
371
372 private void td(String msg, Object o1, Object o2) {
373 if (trace) { // redo test to avoid object creation
374 td(msg + o1 + "[" + o2 + "]");
375 }
376 }
377 private void td(String msg, Object o1) {
378 if (trace) { // redo test to avoid object creation
379 td(msg + o1);
380 }
381 }
382 private void td(String msg) {
383 if (trace) {
384 System.err.println(msg);
385 }
386 }
387}