001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.net.Socket;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.EnumSet;
029 import java.util.HashSet;
030 import java.util.List;
031 import java.util.Set;
032 import java.util.logging.Level;
033 import java.util.concurrent.LinkedBlockingQueue;
034 import java.util.concurrent.TimeUnit;
035 import java.util.concurrent.atomic.AtomicInteger;
036 import java.util.concurrent.atomic.AtomicReference;
037
038 import com.unboundid.ldap.protocol.LDAPResponse;
039 import com.unboundid.ldap.sdk.schema.Schema;
040 import com.unboundid.util.ObjectPair;
041 import com.unboundid.util.ThreadSafety;
042 import com.unboundid.util.ThreadSafetyLevel;
043
044 import static com.unboundid.ldap.sdk.LDAPMessages.*;
045 import static com.unboundid.util.Debug.*;
046 import static com.unboundid.util.StaticUtils.*;
047 import static com.unboundid.util.Validator.*;
048
049
050
051 /**
052 * This class provides an implementation of an LDAP connection pool, which is a
053 * structure that can hold multiple connections established to a given server
054 * that can be reused for multiple operations rather than creating and
055 * destroying connections for each operation. This connection pool
056 * implementation provides traditional methods for checking out and releasing
057 * connections, but it also provides wrapper methods that make it easy to
058 * perform operations using pooled connections without the need to explicitly
059 * check out or release the connections.
060 * <BR><BR>
061 * Note that both the {@code LDAPConnectionPool} class and the
062 * {@link LDAPConnection} class implement the {@link LDAPInterface} interface.
063 * This is a common interface that defines a number of common methods for
064 * processing LDAP requests. This means that in many cases, an application can
065 * use an object of type {@link LDAPInterface} rather than
066 * {@link LDAPConnection}, which makes it possible to work with either a single
067 * standalone connection or with a connection pool.
068 * <BR><BR>
069 * <H2>Creating a Connection Pool</H2>
070 * An LDAP connection pool can be created from either a single
071 * {@link LDAPConnection} (for which an appropriate number of copies will be
072 * created to fill out the pool) or using a {@link ServerSet} to create
073 * connections that may span multiple servers. For example:
074 * <BR><BR>
075 * <PRE>
076 * // Create a new LDAP connection pool with ten connections established and
077 * // authenticated to the same server:
078 * LDAPConnection connection = new LDAPConnection(address, port);
079 * BindResult bindResult = connection.bind(bindDN, password);
080 * LDAPConnectionPool connectionPool = new LDAPConnectionPool(connection, 10);
081 *
082 * // Create a new LDAP connection pool with 10 connections spanning multiple
083 * // servers using a server set.
084 * RoundRobinServerSet serverSet = new RoundRobinServerSet(addresses, ports);
085 * SimpleBindRequest bindRequest = new SimpleBindRequest(bindDN, password);
086 * LDAPConnectionPool connectionPool =
087 * new LDAPConnectionPool(serverSet, bindRequest, 10);
088 * </PRE>
089 * Note that in some cases, such as when using StartTLS, it may be necessary to
090 * perform some additional processing when a new connection is created for use
091 * in the connection pool. In this case, a {@link PostConnectProcessor} should
092 * be provided to accomplish this. See the documentation for the
093 * {@link StartTLSPostConnectProcessor} class for an example that demonstrates
094 * its use for creating a connection pool with connections secured using
095 * StartTLS.
096 * <BR><BR>
097 * <H2>Processing Operations with a Connection Pool</H2>
098 * If a single operation is to be processed using a connection from the
099 * connection pool, then it can be used without the need to check out or release
100 * a connection or perform any validity checking on the connection. This can
101 * be accomplished via the {@link LDAPInterface} interface that allows a
102 * connection pool to be treated like a single connection. For example, to
103 * perform a search using a pooled connection:
104 * <PRE>
105 * SearchResult searchResult =
106 * connectionPool.search("dc=example,dc=com", SearchScope.SUB,
107 * "(uid=john.doe)");
108 * </PRE>
109 * If an application needs to process multiple operations using a single
110 * connection, then it may be beneficial to obtain a connection from the pool
111 * to use for processing those operations and then return it back to the pool
112 * when it is no longer needed. This can be done using the
113 * {@link #getConnection} and {@link #releaseConnection} methods. If during
114 * processing it is determined that the connection is no longer valid, then the
115 * connection should be released back to the pool using the
116 * {@link #releaseDefunctConnection} method, which will ensure that the
117 * connection is closed and a new connection will be established to take its
118 * place in the pool.
119 * <BR><BR>
120 * Note that it is also possible to process multiple operations on a single
121 * connection using the {@link #processRequests} method. This may be useful if
122 * a fixed set of operations should be processed over the same connection and
123 * none of the subsequent requests depend upon the results of the earlier
124 * operations.
125 * <BR><BR>
126 * Connection pools should generally not be used when performing operations that
127 * may change the state of the underlying connections. This is particularly
128 * true for bind operations and the StartTLS extended operation, but it may
129 * apply to other types of operations as well.
130 * <BR><BR>
131 * Performing a bind operation using a connection from the pool will invalidate
132 * any previous authentication on that connection, and if that connection is
133 * released back to the pool without first being re-authenticated as the
134 * original user, then subsequent operation attempts may fail or be processed in
135 * an incorrect manner. Bind operations should only be performed in a
136 * connection pool if the pool is to be used exclusively for processing binds,
137 * if the bind request is specially crafted so that it will not change the
138 * identity of the associated connection (e.g., by including the retain identity
139 * request control in the bind request if using the Commercial Edition of the
140 * LDAP SDK with a Ping Identity, UnboundID, or Alcatel-Lucent 8661 Directory
141 * Server), or if the code using the connection pool makes sure to
142 * re-authenticate the connection as the appropriate user whenever its identity
143 * has been changed.
144 * <BR><BR>
145 * The StartTLS extended operation should never be invoked on a connection which
146 * is part of a connection pool. It is acceptable for the pool to maintain
147 * connections which have been configured with StartTLS security prior to being
148 * added to the pool (via the use of the {@link StartTLSPostConnectProcessor}).
149 * <BR><BR>
150 * <H2>Pool Connection Management</H2>
151 * When creating a connection pool, you may specify an initial number of
152 * connections and a maximum number of connections. The initial number of
153 * connections is the number of connections that should be immediately
154 * established and available for use when the pool is created. The maximum
155 * number of connections is the largest number of unused connections that may
156 * be available in the pool at any time.
157 * <BR><BR>
158 * Whenever a connection is needed, whether by an attempt to check out a
159 * connection or to use one of the pool's methods to process an operation, the
160 * pool will first check to see if there is a connection that has already been
161 * established but is not currently in use, and if so then that connection will
162 * be used. If there aren't any unused connections that are already
163 * established, then the pool will determine if it has yet created the maximum
164 * number of connections, and if not then it will immediately create a new
165 * connection and use it. If the pool has already created the maximum number
166 * of connections, then the pool may wait for a period of time (as indicated by
167 * the {@link #getMaxWaitTimeMillis()} method, which has a default value of zero
168 * to indicate that it should not wait at all) for an in-use connection to be
169 * released back to the pool. If no connection is available after the specified
170 * wait time (or there should not be any wait time), then the pool may
171 * automatically create a new connection to use if
172 * {@link #getCreateIfNecessary()} returns {@code true} (which is the default).
173 * If it is able to successfully create a connection, then it will be used. If
174 * it cannot create a connection, or if {@code getCreateIfNecessary()} returns
175 * {@code false}, then an {@link LDAPException} will be thrown.
176 * <BR><BR>
177 * Note that the maximum number of connections specified when creating a pool
178 * refers to the maximum number of connections that should be available for use
179 * at any given time. If {@code getCreateIfNecessary()} returns {@code true},
180 * then there may temporarily be more active connections than the configured
181 * maximum number of connections. This can be useful during periods of heavy
182 * activity, because the pool will keep those connections established until the
183 * number of unused connections exceeds the configured maximum. If you wish to
184 * enforce a hard limit on the maximum number of connections so that there
185 * cannot be more than the configured maximum in use at any time, then use the
186 * {@link #setCreateIfNecessary(boolean)} method to indicate that the pool
187 * should not automatically create connections when one is needed but none are
188 * available, and you may also want to use the
189 * {@link #setMaxWaitTimeMillis(long)} method to specify a maximum wait time to
190 * allow the pool to wait for a connection to become available rather than
191 * throwing an exception if no connections are immediately available.
192 */
193 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
194 public final class LDAPConnectionPool
195 extends AbstractConnectionPool
196 {
197 /**
198 * The default health check interval for this connection pool, which is set to
199 * 60000 milliseconds (60 seconds).
200 */
201 private static final long DEFAULT_HEALTH_CHECK_INTERVAL = 60000L;
202
203
204
205 /**
206 * The name of the connection property that may be used to indicate that a
207 * particular connection should have a different maximum connection age than
208 * the default for this pool.
209 */
210 static final String ATTACHMENT_NAME_MAX_CONNECTION_AGE =
211 LDAPConnectionPool.class.getName() + ".maxConnectionAge";
212
213
214
215 // A counter used to keep track of the number of times that the pool failed to
216 // replace a defunct connection. It may also be initialized to the difference
217 // between the initial and maximum number of connections that should be
218 // included in the pool.
219 private final AtomicInteger failedReplaceCount;
220
221 // The types of operations that should be retried if they fail in a manner
222 // that may be the result of a connection that is no longer valid.
223 private final AtomicReference<Set<OperationType>> retryOperationTypes;
224
225 // Indicates whether this connection pool has been closed.
226 private volatile boolean closed;
227
228 // Indicates whether to create a new connection if necessary rather than
229 // waiting for a connection to become available.
230 private boolean createIfNecessary;
231
232 // Indicates whether to check the connection age when releasing a connection
233 // back to the pool.
234 private volatile boolean checkConnectionAgeOnRelease;
235
236 // Indicates whether health check processing for connections in synchronous
237 // mode should include attempting to read with a very short timeout to attempt
238 // to detect closures and unsolicited notifications in a more timely manner.
239 private volatile boolean trySynchronousReadDuringHealthCheck;
240
241 // The bind request to use to perform authentication whenever a new connection
242 // is established.
243 private final BindRequest bindRequest;
244
245 // The number of connections to be held in this pool.
246 private final int numConnections;
247
248 // The minimum number of connections that the health check mechanism should
249 // try to keep available for immediate use.
250 private volatile int minConnectionGoal;
251
252 // The health check implementation that should be used for this connection
253 // pool.
254 private LDAPConnectionPoolHealthCheck healthCheck;
255
256 // The thread that will be used to perform periodic background health checks
257 // for this connection pool.
258 private final LDAPConnectionPoolHealthCheckThread healthCheckThread;
259
260 // The statistics for this connection pool.
261 private final LDAPConnectionPoolStatistics poolStatistics;
262
263 // The set of connections that are currently available for use.
264 private final LinkedBlockingQueue<LDAPConnection> availableConnections;
265
266 // The length of time in milliseconds between periodic health checks against
267 // the available connections in this pool.
268 private volatile long healthCheckInterval;
269
270 // The time that the last expired connection was closed.
271 private volatile long lastExpiredDisconnectTime;
272
273 // The maximum length of time in milliseconds that a connection should be
274 // allowed to be established before terminating and re-establishing the
275 // connection.
276 private volatile long maxConnectionAge;
277
278 // The maximum connection age that should be used for connections created to
279 // replace connections that are released as defunct.
280 private volatile Long maxDefunctReplacementConnectionAge;
281
282 // The maximum length of time in milliseconds to wait for a connection to be
283 // available.
284 private long maxWaitTime;
285
286 // The minimum length of time in milliseconds that must pass between
287 // disconnects of connections that have exceeded the maximum connection age.
288 private volatile long minDisconnectInterval;
289
290 // The schema that should be shared for connections in this pool, along with
291 // its expiration time.
292 private volatile ObjectPair<Long,Schema> pooledSchema;
293
294 // The post-connect processor for this connection pool, if any.
295 private final PostConnectProcessor postConnectProcessor;
296
297 // The server set to use for establishing connections for use by this pool.
298 private final ServerSet serverSet;
299
300 // The user-friendly name assigned to this connection pool.
301 private String connectionPoolName;
302
303
304
305
306 /**
307 * Creates a new LDAP connection pool with up to the specified number of
308 * connections, created as clones of the provided connection. Initially, only
309 * the provided connection will be included in the pool, but additional
310 * connections will be created as needed until the pool has reached its full
311 * capacity, at which point the create if necessary and max wait time settings
312 * will be used to determine how to behave if a connection is requested but
313 * none are available.
314 *
315 * @param connection The connection to use to provide the template for
316 * the other connections to be created. This
317 * connection will be included in the pool. It must
318 * not be {@code null}, and it must be established to
319 * the target server. It does not necessarily need to
320 * be authenticated if all connections in the pool are
321 * to be unauthenticated.
322 * @param numConnections The total number of connections that should be
323 * created in the pool. It must be greater than or
324 * equal to one.
325 *
326 * @throws LDAPException If the provided connection cannot be used to
327 * initialize the pool, or if a problem occurs while
328 * attempting to establish any of the connections. If
329 * this is thrown, then all connections associated
330 * with the pool (including the one provided as an
331 * argument) will be closed.
332 */
333 public LDAPConnectionPool(final LDAPConnection connection,
334 final int numConnections)
335 throws LDAPException
336 {
337 this(connection, 1, numConnections, null);
338 }
339
340
341
342 /**
343 * Creates a new LDAP connection pool with the specified number of
344 * connections, created as clones of the provided connection.
345 *
346 * @param connection The connection to use to provide the template
347 * for the other connections to be created. This
348 * connection will be included in the pool. It
349 * must not be {@code null}, and it must be
350 * established to the target server. It does not
351 * necessarily need to be authenticated if all
352 * connections in the pool are to be
353 * unauthenticated.
354 * @param initialConnections The number of connections to initially
355 * establish when the pool is created. It must be
356 * greater than or equal to one.
357 * @param maxConnections The maximum number of connections that should
358 * be maintained in the pool. It must be greater
359 * than or equal to the initial number of
360 * connections. See the "Pool Connection
361 * Management" section of the class-level
362 * documentation for an explanation of how the
363 * pool treats the maximum number of connections.
364 *
365 * @throws LDAPException If the provided connection cannot be used to
366 * initialize the pool, or if a problem occurs while
367 * attempting to establish any of the connections. If
368 * this is thrown, then all connections associated
369 * with the pool (including the one provided as an
370 * argument) will be closed.
371 */
372 public LDAPConnectionPool(final LDAPConnection connection,
373 final int initialConnections,
374 final int maxConnections)
375 throws LDAPException
376 {
377 this(connection, initialConnections, maxConnections, null);
378 }
379
380
381
382 /**
383 * Creates a new LDAP connection pool with the specified number of
384 * connections, created as clones of the provided connection.
385 *
386 * @param connection The connection to use to provide the template
387 * for the other connections to be created.
388 * This connection will be included in the pool.
389 * It must not be {@code null}, and it must be
390 * established to the target server. It does
391 * not necessarily need to be authenticated if
392 * all connections in the pool are to be
393 * unauthenticated.
394 * @param initialConnections The number of connections to initially
395 * establish when the pool is created. It must
396 * be greater than or equal to one.
397 * @param maxConnections The maximum number of connections that should
398 * be maintained in the pool. It must be
399 * greater than or equal to the initial number
400 * of connections. See the "Pool Connection
401 * Management" section of the class-level
402 * documentation for an explanation of how the
403 * pool treats the maximum number of
404 * connections.
405 * @param postConnectProcessor A processor that should be used to perform
406 * any post-connect processing for connections
407 * in this pool. It may be {@code null} if no
408 * special processing is needed. Note that this
409 * processing will not be invoked on the
410 * provided connection that will be used as the
411 * first connection in the pool.
412 *
413 * @throws LDAPException If the provided connection cannot be used to
414 * initialize the pool, or if a problem occurs while
415 * attempting to establish any of the connections. If
416 * this is thrown, then all connections associated
417 * with the pool (including the one provided as an
418 * argument) will be closed.
419 */
420 public LDAPConnectionPool(final LDAPConnection connection,
421 final int initialConnections,
422 final int maxConnections,
423 final PostConnectProcessor postConnectProcessor)
424 throws LDAPException
425 {
426 this(connection, initialConnections, maxConnections, postConnectProcessor,
427 true);
428 }
429
430
431
432 /**
433 * Creates a new LDAP connection pool with the specified number of
434 * connections, created as clones of the provided connection.
435 *
436 * @param connection The connection to use to provide the
437 * template for the other connections to be
438 * created. This connection will be included
439 * in the pool. It must not be {@code null},
440 * and it must be established to the target
441 * server. It does not necessarily need to be
442 * authenticated if all connections in the pool
443 * are to be unauthenticated.
444 * @param initialConnections The number of connections to initially
445 * establish when the pool is created. It must
446 * be greater than or equal to one.
447 * @param maxConnections The maximum number of connections that
448 * should be maintained in the pool. It must
449 * be greater than or equal to the initial
450 * number of connections. See the "Pool
451 * Connection Management" section of the
452 * class-level documentation for an explanation
453 * of how the pool treats the maximum number of
454 * connections.
455 * @param postConnectProcessor A processor that should be used to perform
456 * any post-connect processing for connections
457 * in this pool. It may be {@code null} if no
458 * special processing is needed. Note that
459 * this processing will not be invoked on the
460 * provided connection that will be used as the
461 * first connection in the pool.
462 * @param throwOnConnectFailure If an exception should be thrown if a
463 * problem is encountered while attempting to
464 * create the specified initial number of
465 * connections. If {@code true}, then the
466 * attempt to create the pool will fail.if any
467 * connection cannot be established. If
468 * {@code false}, then the pool will be created
469 * but may have fewer than the initial number
470 * of connections (or possibly no connections).
471 *
472 * @throws LDAPException If the provided connection cannot be used to
473 * initialize the pool, or if a problem occurs while
474 * attempting to establish any of the connections. If
475 * this is thrown, then all connections associated
476 * with the pool (including the one provided as an
477 * argument) will be closed.
478 */
479 public LDAPConnectionPool(final LDAPConnection connection,
480 final int initialConnections,
481 final int maxConnections,
482 final PostConnectProcessor postConnectProcessor,
483 final boolean throwOnConnectFailure)
484 throws LDAPException
485 {
486 this(connection, initialConnections, maxConnections, 1,
487 postConnectProcessor, throwOnConnectFailure);
488 }
489
490
491
492 /**
493 * Creates a new LDAP connection pool with the specified number of
494 * connections, created as clones of the provided connection.
495 *
496 * @param connection The connection to use to provide the
497 * template for the other connections to be
498 * created. This connection will be included
499 * in the pool. It must not be {@code null},
500 * and it must be established to the target
501 * server. It does not necessarily need to be
502 * authenticated if all connections in the pool
503 * are to be unauthenticated.
504 * @param initialConnections The number of connections to initially
505 * establish when the pool is created. It must
506 * be greater than or equal to one.
507 * @param maxConnections The maximum number of connections that
508 * should be maintained in the pool. It must
509 * be greater than or equal to the initial
510 * number of connections. See the "Pool
511 * Connection Management" section of the
512 * class-level documentation for an
513 * explanation of how the pool treats the
514 * maximum number of connections.
515 * @param initialConnectThreads The number of concurrent threads to use to
516 * establish the initial set of connections.
517 * A value greater than one indicates that the
518 * attempt to establish connections should be
519 * parallelized.
520 * @param postConnectProcessor A processor that should be used to perform
521 * any post-connect processing for connections
522 * in this pool. It may be {@code null} if no
523 * special processing is needed. Note that
524 * this processing will not be invoked on the
525 * provided connection that will be used as the
526 * first connection in the pool.
527 * @param throwOnConnectFailure If an exception should be thrown if a
528 * problem is encountered while attempting to
529 * create the specified initial number of
530 * connections. If {@code true}, then the
531 * attempt to create the pool will fail.if any
532 * connection cannot be established. If
533 * {@code false}, then the pool will be created
534 * but may have fewer than the initial number
535 * of connections (or possibly no connections).
536 *
537 * @throws LDAPException If the provided connection cannot be used to
538 * initialize the pool, or if a problem occurs while
539 * attempting to establish any of the connections. If
540 * this is thrown, then all connections associated
541 * with the pool (including the one provided as an
542 * argument) will be closed.
543 */
544 public LDAPConnectionPool(final LDAPConnection connection,
545 final int initialConnections,
546 final int maxConnections,
547 final int initialConnectThreads,
548 final PostConnectProcessor postConnectProcessor,
549 final boolean throwOnConnectFailure)
550 throws LDAPException
551 {
552 this(connection, initialConnections, maxConnections, initialConnectThreads,
553 postConnectProcessor, throwOnConnectFailure, null);
554 }
555
556
557
558 /**
559 * Creates a new LDAP connection pool with the specified number of
560 * connections, created as clones of the provided connection.
561 *
562 * @param connection The connection to use to provide the
563 * template for the other connections to be
564 * created. This connection will be included
565 * in the pool. It must not be {@code null},
566 * and it must be established to the target
567 * server. It does not necessarily need to be
568 * authenticated if all connections in the pool
569 * are to be unauthenticated.
570 * @param initialConnections The number of connections to initially
571 * establish when the pool is created. It must
572 * be greater than or equal to one.
573 * @param maxConnections The maximum number of connections that
574 * should be maintained in the pool. It must
575 * be greater than or equal to the initial
576 * number of connections. See the "Pool
577 * Connection Management" section of the
578 * class-level documentation for an explanation
579 * of how the pool treats the maximum number of
580 * connections.
581 * @param initialConnectThreads The number of concurrent threads to use to
582 * establish the initial set of connections.
583 * A value greater than one indicates that the
584 * attempt to establish connections should be
585 * parallelized.
586 * @param postConnectProcessor A processor that should be used to perform
587 * any post-connect processing for connections
588 * in this pool. It may be {@code null} if no
589 * special processing is needed. Note that
590 * this processing will not be invoked on the
591 * provided connection that will be used as the
592 * first connection in the pool.
593 * @param throwOnConnectFailure If an exception should be thrown if a
594 * problem is encountered while attempting to
595 * create the specified initial number of
596 * connections. If {@code true}, then the
597 * attempt to create the pool will fail.if any
598 * connection cannot be established. If
599 * {@code false}, then the pool will be created
600 * but may have fewer than the initial number
601 * of connections (or possibly no connections).
602 * @param healthCheck The health check that should be used for
603 * connections in this pool. It may be
604 * {@code null} if the default health check
605 * should be used.
606 *
607 * @throws LDAPException If the provided connection cannot be used to
608 * initialize the pool, or if a problem occurs while
609 * attempting to establish any of the connections. If
610 * this is thrown, then all connections associated
611 * with the pool (including the one provided as an
612 * argument) will be closed.
613 */
614 public LDAPConnectionPool(final LDAPConnection connection,
615 final int initialConnections,
616 final int maxConnections,
617 final int initialConnectThreads,
618 final PostConnectProcessor postConnectProcessor,
619 final boolean throwOnConnectFailure,
620 final LDAPConnectionPoolHealthCheck healthCheck)
621 throws LDAPException
622 {
623 ensureNotNull(connection);
624 ensureTrue(initialConnections >= 1,
625 "LDAPConnectionPool.initialConnections must be at least 1.");
626 ensureTrue(maxConnections >= initialConnections,
627 "LDAPConnectionPool.initialConnections must not be greater " +
628 "than maxConnections.");
629
630 this.postConnectProcessor = postConnectProcessor;
631
632 trySynchronousReadDuringHealthCheck = true;
633 healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
634 poolStatistics = new LDAPConnectionPoolStatistics(this);
635 pooledSchema = null;
636 connectionPoolName = null;
637 retryOperationTypes = new AtomicReference<Set<OperationType>>(
638 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
639 numConnections = maxConnections;
640 minConnectionGoal = 0;
641 availableConnections =
642 new LinkedBlockingQueue<LDAPConnection>(numConnections);
643
644 if (! connection.isConnected())
645 {
646 throw new LDAPException(ResultCode.PARAM_ERROR,
647 ERR_POOL_CONN_NOT_ESTABLISHED.get());
648 }
649
650 if (healthCheck == null)
651 {
652 this.healthCheck = new LDAPConnectionPoolHealthCheck();
653 }
654 else
655 {
656 this.healthCheck = healthCheck;
657 }
658
659
660 serverSet = new SingleServerSet(connection.getConnectedAddress(),
661 connection.getConnectedPort(),
662 connection.getLastUsedSocketFactory(),
663 connection.getConnectionOptions());
664 bindRequest = connection.getLastBindRequest();
665
666 final LDAPConnectionOptions opts = connection.getConnectionOptions();
667 if (opts.usePooledSchema())
668 {
669 try
670 {
671 final Schema schema = connection.getSchema();
672 if (schema != null)
673 {
674 connection.setCachedSchema(schema);
675
676 final long currentTime = System.currentTimeMillis();
677 final long timeout = opts.getPooledSchemaTimeoutMillis();
678 if ((timeout <= 0L) || (timeout+currentTime <= 0L))
679 {
680 pooledSchema = new ObjectPair<Long,Schema>(Long.MAX_VALUE, schema);
681 }
682 else
683 {
684 pooledSchema =
685 new ObjectPair<Long,Schema>(timeout+currentTime, schema);
686 }
687 }
688 }
689 catch (final Exception e)
690 {
691 debugException(e);
692 }
693 }
694
695 final List<LDAPConnection> connList;
696 if (initialConnectThreads > 1)
697 {
698 connList = Collections.synchronizedList(
699 new ArrayList<LDAPConnection>(initialConnections));
700 final ParallelPoolConnector connector = new ParallelPoolConnector(this,
701 connList, initialConnections, initialConnectThreads,
702 throwOnConnectFailure);
703 connector.establishConnections();
704 }
705 else
706 {
707 connList = new ArrayList<LDAPConnection>(initialConnections);
708 connection.setConnectionName(null);
709 connection.setConnectionPool(this);
710 connList.add(connection);
711 for (int i=1; i < initialConnections; i++)
712 {
713 try
714 {
715 connList.add(createConnection());
716 }
717 catch (LDAPException le)
718 {
719 debugException(le);
720
721 if (throwOnConnectFailure)
722 {
723 for (final LDAPConnection c : connList)
724 {
725 try
726 {
727 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null,
728 le);
729 c.terminate(null);
730 }
731 catch (Exception e)
732 {
733 debugException(e);
734 }
735 }
736
737 throw le;
738 }
739 }
740 }
741 }
742
743 availableConnections.addAll(connList);
744
745 failedReplaceCount =
746 new AtomicInteger(maxConnections - availableConnections.size());
747 createIfNecessary = true;
748 checkConnectionAgeOnRelease = false;
749 maxConnectionAge = 0L;
750 maxDefunctReplacementConnectionAge = null;
751 minDisconnectInterval = 0L;
752 lastExpiredDisconnectTime = 0L;
753 maxWaitTime = 0L;
754 closed = false;
755
756 healthCheckThread = new LDAPConnectionPoolHealthCheckThread(this);
757 healthCheckThread.start();
758 }
759
760
761
762 /**
763 * Creates a new LDAP connection pool with the specified number of
764 * connections, created using the provided server set. Initially, only
765 * one will be created and included in the pool, but additional connections
766 * will be created as needed until the pool has reached its full capacity, at
767 * which point the create if necessary and max wait time settings will be used
768 * to determine how to behave if a connection is requested but none are
769 * available.
770 *
771 * @param serverSet The server set to use to create the connections.
772 * It is acceptable for the server set to create the
773 * connections across multiple servers.
774 * @param bindRequest The bind request to use to authenticate the
775 * connections that are established. It may be
776 * {@code null} if no authentication should be
777 * performed on the connections.
778 * @param numConnections The total number of connections that should be
779 * created in the pool. It must be greater than or
780 * equal to one.
781 *
782 * @throws LDAPException If a problem occurs while attempting to establish
783 * any of the connections. If this is thrown, then
784 * all connections associated with the pool will be
785 * closed.
786 */
787 public LDAPConnectionPool(final ServerSet serverSet,
788 final BindRequest bindRequest,
789 final int numConnections)
790 throws LDAPException
791 {
792 this(serverSet, bindRequest, 1, numConnections, null);
793 }
794
795
796
797 /**
798 * Creates a new LDAP connection pool with the specified number of
799 * connections, created using the provided server set.
800 *
801 * @param serverSet The server set to use to create the
802 * connections. It is acceptable for the server
803 * set to create the connections across multiple
804 * servers.
805 * @param bindRequest The bind request to use to authenticate the
806 * connections that are established. It may be
807 * {@code null} if no authentication should be
808 * performed on the connections.
809 * @param initialConnections The number of connections to initially
810 * establish when the pool is created. It must be
811 * greater than or equal to zero.
812 * @param maxConnections The maximum number of connections that should
813 * be maintained in the pool. It must be greater
814 * than or equal to the initial number of
815 * connections, and must not be zero. See the
816 * "Pool Connection Management" section of the
817 * class-level documentation for an explanation of
818 * how the pool treats the maximum number of
819 * connections.
820 *
821 * @throws LDAPException If a problem occurs while attempting to establish
822 * any of the connections. If this is thrown, then
823 * all connections associated with the pool will be
824 * closed.
825 */
826 public LDAPConnectionPool(final ServerSet serverSet,
827 final BindRequest bindRequest,
828 final int initialConnections,
829 final int maxConnections)
830 throws LDAPException
831 {
832 this(serverSet, bindRequest, initialConnections, maxConnections, null);
833 }
834
835
836
837 /**
838 * Creates a new LDAP connection pool with the specified number of
839 * connections, created using the provided server set.
840 *
841 * @param serverSet The server set to use to create the
842 * connections. It is acceptable for the server
843 * set to create the connections across multiple
844 * servers.
845 * @param bindRequest The bind request to use to authenticate the
846 * connections that are established. It may be
847 * {@code null} if no authentication should be
848 * performed on the connections.
849 * @param initialConnections The number of connections to initially
850 * establish when the pool is created. It must
851 * be greater than or equal to zero.
852 * @param maxConnections The maximum number of connections that should
853 * be maintained in the pool. It must be
854 * greater than or equal to the initial number
855 * of connections, and must not be zero. See
856 * the "Pool Connection Management" section of
857 * the class-level documentation for an
858 * explanation of how the pool treats the
859 * maximum number of connections.
860 * @param postConnectProcessor A processor that should be used to perform
861 * any post-connect processing for connections
862 * in this pool. It may be {@code null} if no
863 * special processing is needed.
864 *
865 * @throws LDAPException If a problem occurs while attempting to establish
866 * any of the connections. If this is thrown, then
867 * all connections associated with the pool will be
868 * closed.
869 */
870 public LDAPConnectionPool(final ServerSet serverSet,
871 final BindRequest bindRequest,
872 final int initialConnections,
873 final int maxConnections,
874 final PostConnectProcessor postConnectProcessor)
875 throws LDAPException
876 {
877 this(serverSet, bindRequest, initialConnections, maxConnections,
878 postConnectProcessor, true);
879 }
880
881
882
883 /**
884 * Creates a new LDAP connection pool with the specified number of
885 * connections, created using the provided server set.
886 *
887 * @param serverSet The server set to use to create the
888 * connections. It is acceptable for the
889 * server set to create the connections across
890 * multiple servers.
891 * @param bindRequest The bind request to use to authenticate the
892 * connections that are established. It may be
893 * {@code null} if no authentication should be
894 * performed on the connections.
895 * @param initialConnections The number of connections to initially
896 * establish when the pool is created. It must
897 * be greater than or equal to zero.
898 * @param maxConnections The maximum number of connections that
899 * should be maintained in the pool. It must
900 * be greater than or equal to the initial
901 * number of connections, and must not be zero.
902 * See the "Pool Connection Management" section
903 * of the class-level documentation for an
904 * explanation of how the pool treats the
905 * maximum number of connections.
906 * @param postConnectProcessor A processor that should be used to perform
907 * any post-connect processing for connections
908 * in this pool. It may be {@code null} if no
909 * special processing is needed.
910 * @param throwOnConnectFailure If an exception should be thrown if a
911 * problem is encountered while attempting to
912 * create the specified initial number of
913 * connections. If {@code true}, then the
914 * attempt to create the pool will fail.if any
915 * connection cannot be established. If
916 * {@code false}, then the pool will be created
917 * but may have fewer than the initial number
918 * of connections (or possibly no connections).
919 *
920 * @throws LDAPException If a problem occurs while attempting to establish
921 * any of the connections and
922 * {@code throwOnConnectFailure} is true. If this is
923 * thrown, then all connections associated with the
924 * pool will be closed.
925 */
926 public LDAPConnectionPool(final ServerSet serverSet,
927 final BindRequest bindRequest,
928 final int initialConnections,
929 final int maxConnections,
930 final PostConnectProcessor postConnectProcessor,
931 final boolean throwOnConnectFailure)
932 throws LDAPException
933 {
934 this(serverSet, bindRequest, initialConnections, maxConnections, 1,
935 postConnectProcessor, throwOnConnectFailure);
936 }
937
938
939
940 /**
941 * Creates a new LDAP connection pool with the specified number of
942 * connections, created using the provided server set.
943 *
944 * @param serverSet The server set to use to create the
945 * connections. It is acceptable for the
946 * server set to create the connections across
947 * multiple servers.
948 * @param bindRequest The bind request to use to authenticate the
949 * connections that are established. It may be
950 * {@code null} if no authentication should be
951 * performed on the connections.
952 * @param initialConnections The number of connections to initially
953 * establish when the pool is created. It must
954 * be greater than or equal to zero.
955 * @param maxConnections The maximum number of connections that
956 * should be maintained in the pool. It must
957 * be greater than or equal to the initial
958 * number of connections, and must not be zero.
959 * See the "Pool Connection Management" section
960 * of the class-level documentation for an
961 * explanation of how the pool treats the
962 * maximum number of connections.
963 * @param initialConnectThreads The number of concurrent threads to use to
964 * establish the initial set of connections.
965 * A value greater than one indicates that the
966 * attempt to establish connections should be
967 * parallelized.
968 * @param postConnectProcessor A processor that should be used to perform
969 * any post-connect processing for connections
970 * in this pool. It may be {@code null} if no
971 * special processing is needed.
972 * @param throwOnConnectFailure If an exception should be thrown if a
973 * problem is encountered while attempting to
974 * create the specified initial number of
975 * connections. If {@code true}, then the
976 * attempt to create the pool will fail.if any
977 * connection cannot be established. If
978 * {@code false}, then the pool will be created
979 * but may have fewer than the initial number
980 * of connections (or possibly no connections).
981 *
982 * @throws LDAPException If a problem occurs while attempting to establish
983 * any of the connections and
984 * {@code throwOnConnectFailure} is true. If this is
985 * thrown, then all connections associated with the
986 * pool will be closed.
987 */
988 public LDAPConnectionPool(final ServerSet serverSet,
989 final BindRequest bindRequest,
990 final int initialConnections,
991 final int maxConnections,
992 final int initialConnectThreads,
993 final PostConnectProcessor postConnectProcessor,
994 final boolean throwOnConnectFailure)
995 throws LDAPException
996 {
997 this(serverSet, bindRequest, initialConnections, maxConnections,
998 initialConnectThreads, postConnectProcessor, throwOnConnectFailure,
999 null);
1000 }
1001
1002
1003
1004 /**
1005 * Creates a new LDAP connection pool with the specified number of
1006 * connections, created using the provided server set.
1007 *
1008 * @param serverSet The server set to use to create the
1009 * connections. It is acceptable for the
1010 * server set to create the connections across
1011 * multiple servers.
1012 * @param bindRequest The bind request to use to authenticate the
1013 * connections that are established. It may be
1014 * {@code null} if no authentication should be
1015 * performed on the connections.
1016 * @param initialConnections The number of connections to initially
1017 * establish when the pool is created. It must
1018 * be greater than or equal to zero.
1019 * @param maxConnections The maximum number of connections that
1020 * should be maintained in the pool. It must
1021 * be greater than or equal to the initial
1022 * number of connections, and must not be zero.
1023 * See the "Pool Connection Management" section
1024 * of the class-level documentation for an
1025 * explanation of how the pool treats the
1026 * maximum number of connections.
1027 * @param initialConnectThreads The number of concurrent threads to use to
1028 * establish the initial set of connections.
1029 * A value greater than one indicates that the
1030 * attempt to establish connections should be
1031 * parallelized.
1032 * @param postConnectProcessor A processor that should be used to perform
1033 * any post-connect processing for connections
1034 * in this pool. It may be {@code null} if no
1035 * special processing is needed.
1036 * @param throwOnConnectFailure If an exception should be thrown if a
1037 * problem is encountered while attempting to
1038 * create the specified initial number of
1039 * connections. If {@code true}, then the
1040 * attempt to create the pool will fail if any
1041 * connection cannot be established. If
1042 * {@code false}, then the pool will be created
1043 * but may have fewer than the initial number
1044 * of connections (or possibly no connections).
1045 * @param healthCheck The health check that should be used for
1046 * connections in this pool. It may be
1047 * {@code null} if the default health check
1048 * should be used.
1049 *
1050 * @throws LDAPException If a problem occurs while attempting to establish
1051 * any of the connections and
1052 * {@code throwOnConnectFailure} is true. If this is
1053 * thrown, then all connections associated with the
1054 * pool will be closed.
1055 */
1056 public LDAPConnectionPool(final ServerSet serverSet,
1057 final BindRequest bindRequest,
1058 final int initialConnections,
1059 final int maxConnections,
1060 final int initialConnectThreads,
1061 final PostConnectProcessor postConnectProcessor,
1062 final boolean throwOnConnectFailure,
1063 final LDAPConnectionPoolHealthCheck healthCheck)
1064 throws LDAPException
1065 {
1066 ensureNotNull(serverSet);
1067 ensureTrue(initialConnections >= 0,
1068 "LDAPConnectionPool.initialConnections must be greater than " +
1069 "or equal to 0.");
1070 ensureTrue(maxConnections > 0,
1071 "LDAPConnectionPool.maxConnections must be greater than 0.");
1072 ensureTrue(maxConnections >= initialConnections,
1073 "LDAPConnectionPool.initialConnections must not be greater " +
1074 "than maxConnections.");
1075
1076 this.serverSet = serverSet;
1077 this.bindRequest = bindRequest;
1078 this.postConnectProcessor = postConnectProcessor;
1079
1080 trySynchronousReadDuringHealthCheck = false;
1081 healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
1082 poolStatistics = new LDAPConnectionPoolStatistics(this);
1083 pooledSchema = null;
1084 connectionPoolName = null;
1085 retryOperationTypes = new AtomicReference<Set<OperationType>>(
1086 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
1087 minConnectionGoal = 0;
1088
1089 if (healthCheck == null)
1090 {
1091 this.healthCheck = new LDAPConnectionPoolHealthCheck();
1092 }
1093 else
1094 {
1095 this.healthCheck = healthCheck;
1096 }
1097
1098 final List<LDAPConnection> connList;
1099 if (initialConnectThreads > 1)
1100 {
1101 connList = Collections.synchronizedList(
1102 new ArrayList<LDAPConnection>(initialConnections));
1103 final ParallelPoolConnector connector = new ParallelPoolConnector(this,
1104 connList, initialConnections, initialConnectThreads,
1105 throwOnConnectFailure);
1106 connector.establishConnections();
1107 }
1108 else
1109 {
1110 connList = new ArrayList<LDAPConnection>(initialConnections);
1111 for (int i=0; i < initialConnections; i++)
1112 {
1113 try
1114 {
1115 connList.add(createConnection());
1116 }
1117 catch (LDAPException le)
1118 {
1119 debugException(le);
1120
1121 if (throwOnConnectFailure)
1122 {
1123 for (final LDAPConnection c : connList)
1124 {
1125 try
1126 {
1127 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null,
1128 le);
1129 c.terminate(null);
1130 } catch (Exception e)
1131 {
1132 debugException(e);
1133 }
1134 }
1135
1136 throw le;
1137 }
1138 }
1139 }
1140 }
1141
1142 numConnections = maxConnections;
1143
1144 availableConnections =
1145 new LinkedBlockingQueue<LDAPConnection>(numConnections);
1146 availableConnections.addAll(connList);
1147
1148 failedReplaceCount =
1149 new AtomicInteger(maxConnections - availableConnections.size());
1150 createIfNecessary = true;
1151 checkConnectionAgeOnRelease = false;
1152 maxConnectionAge = 0L;
1153 maxDefunctReplacementConnectionAge = null;
1154 minDisconnectInterval = 0L;
1155 lastExpiredDisconnectTime = 0L;
1156 maxWaitTime = 0L;
1157 closed = false;
1158
1159 healthCheckThread = new LDAPConnectionPoolHealthCheckThread(this);
1160 healthCheckThread.start();
1161 }
1162
1163
1164
1165 /**
1166 * Creates a new LDAP connection for use in this pool.
1167 *
1168 * @return A new connection created for use in this pool.
1169 *
1170 * @throws LDAPException If a problem occurs while attempting to establish
1171 * the connection. If a connection had been created,
1172 * it will be closed.
1173 */
1174 @SuppressWarnings("deprecation")
1175 LDAPConnection createConnection()
1176 throws LDAPException
1177 {
1178 return createConnection(healthCheck);
1179 }
1180
1181
1182
1183 /**
1184 * Creates a new LDAP connection for use in this pool.
1185 *
1186 * @param healthCheck The health check to use to determine whether the
1187 * newly-created connection is valid. It may be
1188 * {@code null} if no additional health checking should
1189 * be performed for the newly-created connection.
1190 *
1191 * @return A new connection created for use in this pool.
1192 *
1193 * @throws LDAPException If a problem occurs while attempting to establish
1194 * the connection. If a connection had been created,
1195 * it will be closed.
1196 */
1197 @SuppressWarnings("deprecation")
1198 private LDAPConnection createConnection(
1199 final LDAPConnectionPoolHealthCheck healthCheck)
1200 throws LDAPException
1201 {
1202 final LDAPConnection c;
1203 try
1204 {
1205 c = serverSet.getConnection(healthCheck);
1206 }
1207 catch (final LDAPException le)
1208 {
1209 debugException(le);
1210 poolStatistics.incrementNumFailedConnectionAttempts();
1211 throw le;
1212 }
1213 c.setConnectionPool(this);
1214
1215
1216 // Auto-reconnect must be disabled for pooled connections, so turn it off
1217 // if the associated connection options have it enabled for some reason.
1218 LDAPConnectionOptions opts = c.getConnectionOptions();
1219 if (opts.autoReconnect())
1220 {
1221 opts = opts.duplicate();
1222 opts.setAutoReconnect(false);
1223 c.setConnectionOptions(opts);
1224 }
1225
1226
1227 // Invoke pre-authentication post-connect processing.
1228 if (postConnectProcessor != null)
1229 {
1230 try
1231 {
1232 postConnectProcessor.processPreAuthenticatedConnection(c);
1233 }
1234 catch (Exception e)
1235 {
1236 debugException(e);
1237
1238 try
1239 {
1240 poolStatistics.incrementNumFailedConnectionAttempts();
1241 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1242 c.terminate(null);
1243 }
1244 catch (Exception e2)
1245 {
1246 debugException(e2);
1247 }
1248
1249 if (e instanceof LDAPException)
1250 {
1251 throw ((LDAPException) e);
1252 }
1253 else
1254 {
1255 throw new LDAPException(ResultCode.CONNECT_ERROR,
1256 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
1257 }
1258 }
1259 }
1260
1261
1262 // Authenticate the connection if appropriate.
1263 BindResult bindResult = null;
1264 try
1265 {
1266 if (bindRequest != null)
1267 {
1268 bindResult = c.bind(bindRequest.duplicate());
1269 }
1270 }
1271 catch (final LDAPBindException lbe)
1272 {
1273 debugException(lbe);
1274 bindResult = lbe.getBindResult();
1275 }
1276 catch (final LDAPException le)
1277 {
1278 debugException(le);
1279 bindResult = new BindResult(le);
1280 }
1281
1282 if (bindResult != null)
1283 {
1284 try
1285 {
1286 healthCheck.ensureConnectionValidAfterAuthentication(c, bindResult);
1287 if (bindResult.getResultCode() != ResultCode.SUCCESS)
1288 {
1289 throw new LDAPBindException(bindResult);
1290 }
1291 }
1292 catch (final LDAPException le)
1293 {
1294 debugException(le);
1295
1296 try
1297 {
1298 poolStatistics.incrementNumFailedConnectionAttempts();
1299 c.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
1300 c.terminate(null);
1301 }
1302 catch (final Exception e)
1303 {
1304 debugException(e);
1305 }
1306
1307 throw le;
1308 }
1309 }
1310
1311
1312 // Invoke post-authentication post-connect processing.
1313 if (postConnectProcessor != null)
1314 {
1315 try
1316 {
1317 postConnectProcessor.processPostAuthenticatedConnection(c);
1318 }
1319 catch (Exception e)
1320 {
1321 debugException(e);
1322 try
1323 {
1324 poolStatistics.incrementNumFailedConnectionAttempts();
1325 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1326 c.terminate(null);
1327 }
1328 catch (Exception e2)
1329 {
1330 debugException(e2);
1331 }
1332
1333 if (e instanceof LDAPException)
1334 {
1335 throw ((LDAPException) e);
1336 }
1337 else
1338 {
1339 throw new LDAPException(ResultCode.CONNECT_ERROR,
1340 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
1341 }
1342 }
1343 }
1344
1345
1346 // Get the pooled schema if appropriate.
1347 if (opts.usePooledSchema())
1348 {
1349 final long currentTime = System.currentTimeMillis();
1350 if ((pooledSchema == null) || (currentTime > pooledSchema.getFirst()))
1351 {
1352 try
1353 {
1354 final Schema schema = c.getSchema();
1355 if (schema != null)
1356 {
1357 c.setCachedSchema(schema);
1358
1359 final long timeout = opts.getPooledSchemaTimeoutMillis();
1360 if ((timeout <= 0L) || (currentTime + timeout <= 0L))
1361 {
1362 pooledSchema =
1363 new ObjectPair<Long,Schema>(Long.MAX_VALUE, schema);
1364 }
1365 else
1366 {
1367 pooledSchema =
1368 new ObjectPair<Long,Schema>((currentTime+timeout), schema);
1369 }
1370 }
1371 }
1372 catch (final Exception e)
1373 {
1374 debugException(e);
1375
1376 // There was a problem retrieving the schema from the server, but if
1377 // we have an earlier copy then we can assume it's still valid.
1378 if (pooledSchema != null)
1379 {
1380 c.setCachedSchema(pooledSchema.getSecond());
1381 }
1382 }
1383 }
1384 else
1385 {
1386 c.setCachedSchema(pooledSchema.getSecond());
1387 }
1388 }
1389
1390
1391 // Finish setting up the connection.
1392 c.setConnectionPoolName(connectionPoolName);
1393 poolStatistics.incrementNumSuccessfulConnectionAttempts();
1394
1395 return c;
1396 }
1397
1398
1399
1400 /**
1401 * {@inheritDoc}
1402 */
1403 @Override()
1404 public void close()
1405 {
1406 close(true, 1);
1407 }
1408
1409
1410
1411 /**
1412 * {@inheritDoc}
1413 */
1414 @Override()
1415 public void close(final boolean unbind, final int numThreads)
1416 {
1417 closed = true;
1418 healthCheckThread.stopRunning();
1419
1420 if (numThreads > 1)
1421 {
1422 final ArrayList<LDAPConnection> connList =
1423 new ArrayList<LDAPConnection>(availableConnections.size());
1424 availableConnections.drainTo(connList);
1425
1426 if (! connList.isEmpty())
1427 {
1428 final ParallelPoolCloser closer =
1429 new ParallelPoolCloser(connList, unbind, numThreads);
1430 closer.closeConnections();
1431 }
1432 }
1433 else
1434 {
1435 while (true)
1436 {
1437 final LDAPConnection conn = availableConnections.poll();
1438 if (conn == null)
1439 {
1440 return;
1441 }
1442 else
1443 {
1444 poolStatistics.incrementNumConnectionsClosedUnneeded();
1445 conn.setDisconnectInfo(DisconnectType.POOL_CLOSED, null, null);
1446 if (unbind)
1447 {
1448 conn.terminate(null);
1449 }
1450 else
1451 {
1452 conn.setClosed();
1453 }
1454 }
1455 }
1456 }
1457 }
1458
1459
1460
1461 /**
1462 * {@inheritDoc}
1463 */
1464 @Override()
1465 public boolean isClosed()
1466 {
1467 return closed;
1468 }
1469
1470
1471
1472 /**
1473 * Processes a simple bind using a connection from this connection pool, and
1474 * then reverts that authentication by re-binding as the same user used to
1475 * authenticate new connections. If new connections are unauthenticated, then
1476 * the subsequent bind will be an anonymous simple bind. This method attempts
1477 * to ensure that processing the provided bind operation does not have a
1478 * lasting impact the authentication state of the connection used to process
1479 * it.
1480 * <BR><BR>
1481 * If the second bind attempt (the one used to restore the authentication
1482 * identity) fails, the connection will be closed as defunct so that a new
1483 * connection will be created to take its place.
1484 *
1485 * @param bindDN The bind DN for the simple bind request.
1486 * @param password The password for the simple bind request.
1487 * @param controls The optional set of controls for the simple bind request.
1488 *
1489 * @return The result of processing the provided bind operation.
1490 *
1491 * @throws LDAPException If the server rejects the bind request, or if a
1492 * problem occurs while sending the request or reading
1493 * the response.
1494 */
1495 public BindResult bindAndRevertAuthentication(final String bindDN,
1496 final String password,
1497 final Control... controls)
1498 throws LDAPException
1499 {
1500 return bindAndRevertAuthentication(
1501 new SimpleBindRequest(bindDN, password, controls));
1502 }
1503
1504
1505
1506 /**
1507 * Processes the provided bind request using a connection from this connection
1508 * pool, and then reverts that authentication by re-binding as the same user
1509 * used to authenticate new connections. If new connections are
1510 * unauthenticated, then the subsequent bind will be an anonymous simple bind.
1511 * This method attempts to ensure that processing the provided bind operation
1512 * does not have a lasting impact the authentication state of the connection
1513 * used to process it.
1514 * <BR><BR>
1515 * If the second bind attempt (the one used to restore the authentication
1516 * identity) fails, the connection will be closed as defunct so that a new
1517 * connection will be created to take its place.
1518 *
1519 * @param bindRequest The bind request to be processed. It must not be
1520 * {@code null}.
1521 *
1522 * @return The result of processing the provided bind operation.
1523 *
1524 * @throws LDAPException If the server rejects the bind request, or if a
1525 * problem occurs while sending the request or reading
1526 * the response.
1527 */
1528 public BindResult bindAndRevertAuthentication(final BindRequest bindRequest)
1529 throws LDAPException
1530 {
1531 LDAPConnection conn = getConnection();
1532
1533 try
1534 {
1535 final BindResult result = conn.bind(bindRequest);
1536 releaseAndReAuthenticateConnection(conn);
1537 return result;
1538 }
1539 catch (final Throwable t)
1540 {
1541 debugException(t);
1542
1543 if (t instanceof LDAPException)
1544 {
1545 final LDAPException le = (LDAPException) t;
1546
1547 boolean shouldThrow;
1548 try
1549 {
1550 healthCheck.ensureConnectionValidAfterException(conn, le);
1551
1552 // The above call will throw an exception if the connection doesn't
1553 // seem to be valid, so if we've gotten here then we should assume
1554 // that it is valid and we will pass the exception onto the client
1555 // without retrying the operation.
1556 releaseAndReAuthenticateConnection(conn);
1557 shouldThrow = true;
1558 }
1559 catch (final Exception e)
1560 {
1561 debugException(e);
1562
1563 // This implies that the connection is not valid. If the pool is
1564 // configured to re-try bind operations on a newly-established
1565 // connection, then that will be done later in this method.
1566 // Otherwise, release the connection as defunct and pass the bind
1567 // exception onto the client.
1568 if (! getOperationTypesToRetryDueToInvalidConnections().contains(
1569 OperationType.BIND))
1570 {
1571 releaseDefunctConnection(conn);
1572 shouldThrow = true;
1573 }
1574 else
1575 {
1576 shouldThrow = false;
1577 }
1578 }
1579
1580 if (shouldThrow)
1581 {
1582 throw le;
1583 }
1584 }
1585 else
1586 {
1587 releaseDefunctConnection(conn);
1588 throw new LDAPException(ResultCode.LOCAL_ERROR,
1589 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t);
1590 }
1591 }
1592
1593
1594 // If we've gotten here, then the bind operation should be re-tried on a
1595 // newly-established connection.
1596 conn = replaceDefunctConnection(conn);
1597
1598 try
1599 {
1600 final BindResult result = conn.bind(bindRequest);
1601 releaseAndReAuthenticateConnection(conn);
1602 return result;
1603 }
1604 catch (final Throwable t)
1605 {
1606 debugException(t);
1607
1608 if (t instanceof LDAPException)
1609 {
1610 final LDAPException le = (LDAPException) t;
1611
1612 try
1613 {
1614 healthCheck.ensureConnectionValidAfterException(conn, le);
1615 releaseAndReAuthenticateConnection(conn);
1616 }
1617 catch (final Exception e)
1618 {
1619 debugException(e);
1620 releaseDefunctConnection(conn);
1621 }
1622
1623 throw le;
1624 }
1625 else
1626 {
1627 releaseDefunctConnection(conn);
1628 throw new LDAPException(ResultCode.LOCAL_ERROR,
1629 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t);
1630 }
1631 }
1632 }
1633
1634
1635
1636 /**
1637 * {@inheritDoc}
1638 */
1639 @Override()
1640 public LDAPConnection getConnection()
1641 throws LDAPException
1642 {
1643 if (closed)
1644 {
1645 poolStatistics.incrementNumFailedCheckouts();
1646 throw new LDAPException(ResultCode.CONNECT_ERROR,
1647 ERR_POOL_CLOSED.get());
1648 }
1649
1650 LDAPConnection conn = availableConnections.poll();
1651 if (conn != null)
1652 {
1653 if (conn.isConnected())
1654 {
1655 try
1656 {
1657 healthCheck.ensureConnectionValidForCheckout(conn);
1658 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1659 return conn;
1660 }
1661 catch (LDAPException le)
1662 {
1663 debugException(le);
1664 }
1665 }
1666
1667 poolStatistics.incrementNumConnectionsClosedDefunct();
1668 handleDefunctConnection(conn);
1669 for (int i=0; i < numConnections; i++)
1670 {
1671 conn = availableConnections.poll();
1672 if (conn == null)
1673 {
1674 break;
1675 }
1676 else if (conn.isConnected())
1677 {
1678 try
1679 {
1680 healthCheck.ensureConnectionValidForCheckout(conn);
1681 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1682 return conn;
1683 }
1684 catch (LDAPException le)
1685 {
1686 debugException(le);
1687 poolStatistics.incrementNumConnectionsClosedDefunct();
1688 handleDefunctConnection(conn);
1689 }
1690 }
1691 else
1692 {
1693 poolStatistics.incrementNumConnectionsClosedDefunct();
1694 handleDefunctConnection(conn);
1695 }
1696 }
1697 }
1698
1699 if (failedReplaceCount.get() > 0)
1700 {
1701 final int newReplaceCount = failedReplaceCount.getAndDecrement();
1702 if (newReplaceCount > 0)
1703 {
1704 try
1705 {
1706 conn = createConnection();
1707 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1708 return conn;
1709 }
1710 catch (LDAPException le)
1711 {
1712 debugException(le);
1713 failedReplaceCount.incrementAndGet();
1714 poolStatistics.incrementNumFailedCheckouts();
1715 throw le;
1716 }
1717 }
1718 else
1719 {
1720 failedReplaceCount.incrementAndGet();
1721 poolStatistics.incrementNumFailedCheckouts();
1722 throw new LDAPException(ResultCode.CONNECT_ERROR,
1723 ERR_POOL_NO_CONNECTIONS.get());
1724 }
1725 }
1726
1727 if (maxWaitTime > 0)
1728 {
1729 try
1730 {
1731 conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS);
1732 if (conn != null)
1733 {
1734 try
1735 {
1736 healthCheck.ensureConnectionValidForCheckout(conn);
1737 poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting();
1738 return conn;
1739 }
1740 catch (LDAPException le)
1741 {
1742 debugException(le);
1743 poolStatistics.incrementNumConnectionsClosedDefunct();
1744 handleDefunctConnection(conn);
1745 }
1746 }
1747 }
1748 catch (InterruptedException ie)
1749 {
1750 debugException(ie);
1751 }
1752 }
1753
1754 if (createIfNecessary)
1755 {
1756 try
1757 {
1758 conn = createConnection();
1759 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1760 return conn;
1761 }
1762 catch (LDAPException le)
1763 {
1764 debugException(le);
1765 poolStatistics.incrementNumFailedCheckouts();
1766 throw le;
1767 }
1768 }
1769 else
1770 {
1771 poolStatistics.incrementNumFailedCheckouts();
1772 throw new LDAPException(ResultCode.CONNECT_ERROR,
1773 ERR_POOL_NO_CONNECTIONS.get());
1774 }
1775 }
1776
1777
1778
1779 /**
1780 * Attempts to retrieve a connection from the pool that is established to the
1781 * specified server. Note that this method will only attempt to return an
1782 * existing connection that is currently available, and will not create a
1783 * connection or wait for any checked-out connections to be returned.
1784 *
1785 * @param host The address of the server to which the desired connection
1786 * should be established. This must not be {@code null}, and
1787 * this must exactly match the address provided for the initial
1788 * connection or the {@code ServerSet} used to create the pool.
1789 * @param port The port of the server to which the desired connection should
1790 * be established.
1791 *
1792 * @return A connection that is established to the specified server, or
1793 * {@code null} if there are no available connections established to
1794 * the specified server.
1795 */
1796 public LDAPConnection getConnection(final String host, final int port)
1797 {
1798 if (closed)
1799 {
1800 poolStatistics.incrementNumFailedCheckouts();
1801 return null;
1802 }
1803
1804 final HashSet<LDAPConnection> examinedConnections =
1805 new HashSet<LDAPConnection>(numConnections);
1806 while (true)
1807 {
1808 final LDAPConnection conn = availableConnections.poll();
1809 if (conn == null)
1810 {
1811 poolStatistics.incrementNumFailedCheckouts();
1812 return null;
1813 }
1814
1815 if (examinedConnections.contains(conn))
1816 {
1817 availableConnections.offer(conn);
1818 poolStatistics.incrementNumFailedCheckouts();
1819 return null;
1820 }
1821
1822 if (conn.getConnectedAddress().equals(host) &&
1823 (port == conn.getConnectedPort()))
1824 {
1825 try
1826 {
1827 healthCheck.ensureConnectionValidForCheckout(conn);
1828 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1829 return conn;
1830 }
1831 catch (final LDAPException le)
1832 {
1833 debugException(le);
1834 poolStatistics.incrementNumConnectionsClosedDefunct();
1835 handleDefunctConnection(conn);
1836 continue;
1837 }
1838 }
1839
1840 if (availableConnections.offer(conn))
1841 {
1842 examinedConnections.add(conn);
1843 }
1844 }
1845 }
1846
1847
1848
1849 /**
1850 * {@inheritDoc}
1851 */
1852 @Override()
1853 public void releaseConnection(final LDAPConnection connection)
1854 {
1855 if (connection == null)
1856 {
1857 return;
1858 }
1859
1860 connection.setConnectionPoolName(connectionPoolName);
1861 if (checkConnectionAgeOnRelease && connectionIsExpired(connection))
1862 {
1863 try
1864 {
1865 final LDAPConnection newConnection = createConnection();
1866 if (availableConnections.offer(newConnection))
1867 {
1868 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
1869 null, null);
1870 connection.terminate(null);
1871 poolStatistics.incrementNumConnectionsClosedExpired();
1872 lastExpiredDisconnectTime = System.currentTimeMillis();
1873 }
1874 else
1875 {
1876 newConnection.setDisconnectInfo(
1877 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
1878 newConnection.terminate(null);
1879 poolStatistics.incrementNumConnectionsClosedUnneeded();
1880 }
1881 }
1882 catch (final LDAPException le)
1883 {
1884 debugException(le);
1885 }
1886 return;
1887 }
1888
1889 try
1890 {
1891 healthCheck.ensureConnectionValidForRelease(connection);
1892 }
1893 catch (LDAPException le)
1894 {
1895 releaseDefunctConnection(connection);
1896 return;
1897 }
1898
1899 if (availableConnections.offer(connection))
1900 {
1901 poolStatistics.incrementNumReleasedValid();
1902 }
1903 else
1904 {
1905 // This means that the connection pool is full, which can happen if the
1906 // pool was empty when a request came in to retrieve a connection and
1907 // createIfNecessary was true. In this case, we'll just close the
1908 // connection since we don't need it any more.
1909 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1910 null, null);
1911 poolStatistics.incrementNumConnectionsClosedUnneeded();
1912 connection.terminate(null);
1913 return;
1914 }
1915
1916 if (closed)
1917 {
1918 close();
1919 }
1920 }
1921
1922
1923
1924 /**
1925 * Indicates that the provided connection should be removed from the pool,
1926 * and that no new connection should be created to take its place. This may
1927 * be used to shrink the pool if such functionality is desired.
1928 *
1929 * @param connection The connection to be discarded.
1930 */
1931 public void discardConnection(final LDAPConnection connection)
1932 {
1933 if (connection == null)
1934 {
1935 return;
1936 }
1937
1938 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1939 null, null);
1940 connection.terminate(null);
1941 poolStatistics.incrementNumConnectionsClosedUnneeded();
1942
1943 if (availableConnections.remainingCapacity() > 0)
1944 {
1945 final int newReplaceCount = failedReplaceCount.incrementAndGet();
1946 if (newReplaceCount > numConnections)
1947 {
1948 failedReplaceCount.set(numConnections);
1949 }
1950 }
1951 }
1952
1953
1954
1955 /**
1956 * Performs a bind on the provided connection before releasing it back to the
1957 * pool, so that it will be authenticated as the same user as
1958 * newly-established connections. If newly-established connections are
1959 * unauthenticated, then this method will perform an anonymous simple bind to
1960 * ensure that the resulting connection is unauthenticated.
1961 *
1962 * Releases the provided connection back to this pool.
1963 *
1964 * @param connection The connection to be released back to the pool after
1965 * being re-authenticated.
1966 */
1967 public void releaseAndReAuthenticateConnection(
1968 final LDAPConnection connection)
1969 {
1970 if (connection == null)
1971 {
1972 return;
1973 }
1974
1975 try
1976 {
1977 BindResult bindResult;
1978 try
1979 {
1980 if (bindRequest == null)
1981 {
1982 bindResult = connection.bind("", "");
1983 }
1984 else
1985 {
1986 bindResult = connection.bind(bindRequest.duplicate());
1987 }
1988 }
1989 catch (final LDAPBindException lbe)
1990 {
1991 debugException(lbe);
1992 bindResult = lbe.getBindResult();
1993 }
1994
1995 try
1996 {
1997 healthCheck.ensureConnectionValidAfterAuthentication(connection,
1998 bindResult);
1999 if (bindResult.getResultCode() != ResultCode.SUCCESS)
2000 {
2001 throw new LDAPBindException(bindResult);
2002 }
2003 }
2004 catch (final LDAPException le)
2005 {
2006 debugException(le);
2007
2008 try
2009 {
2010 connection.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
2011 connection.terminate(null);
2012 releaseDefunctConnection(connection);
2013 }
2014 catch (final Exception e)
2015 {
2016 debugException(e);
2017 }
2018
2019 throw le;
2020 }
2021
2022 releaseConnection(connection);
2023 }
2024 catch (final Exception e)
2025 {
2026 debugException(e);
2027 releaseDefunctConnection(connection);
2028 }
2029 }
2030
2031
2032
2033 /**
2034 * {@inheritDoc}
2035 */
2036 @Override()
2037 public void releaseDefunctConnection(final LDAPConnection connection)
2038 {
2039 if (connection == null)
2040 {
2041 return;
2042 }
2043
2044 connection.setConnectionPoolName(connectionPoolName);
2045 poolStatistics.incrementNumConnectionsClosedDefunct();
2046 handleDefunctConnection(connection);
2047 }
2048
2049
2050
2051 /**
2052 * Performs the real work of terminating a defunct connection and replacing it
2053 * with a new connection if possible.
2054 *
2055 * @param connection The defunct connection to be replaced.
2056 *
2057 * @return The new connection created to take the place of the defunct
2058 * connection, or {@code null} if no new connection was created.
2059 * Note that if a connection is returned, it will have already been
2060 * made available and the caller must not rely on it being unused for
2061 * any other purpose.
2062 */
2063 private LDAPConnection handleDefunctConnection(
2064 final LDAPConnection connection)
2065 {
2066 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2067 null);
2068 connection.terminate(null);
2069
2070 if (closed)
2071 {
2072 return null;
2073 }
2074
2075 if (createIfNecessary && (availableConnections.remainingCapacity() <= 0))
2076 {
2077 return null;
2078 }
2079
2080 try
2081 {
2082 final LDAPConnection conn = createConnection();
2083 if (maxDefunctReplacementConnectionAge != null)
2084 {
2085 // Only set the maximum age if there isn't one already set for the
2086 // connection (i.e., because it was defined by the server set).
2087 if (conn.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE) == null)
2088 {
2089 conn.setAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE,
2090 maxDefunctReplacementConnectionAge);
2091 }
2092 }
2093
2094 if (! availableConnections.offer(conn))
2095 {
2096 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2097 null, null);
2098 conn.terminate(null);
2099 return null;
2100 }
2101
2102 return conn;
2103 }
2104 catch (LDAPException le)
2105 {
2106 debugException(le);
2107 final int newReplaceCount = failedReplaceCount.incrementAndGet();
2108 if (newReplaceCount > numConnections)
2109 {
2110 failedReplaceCount.set(numConnections);
2111 }
2112 return null;
2113 }
2114 }
2115
2116
2117
2118 /**
2119 * {@inheritDoc}
2120 */
2121 @Override()
2122 public LDAPConnection replaceDefunctConnection(
2123 final LDAPConnection connection)
2124 throws LDAPException
2125 {
2126 poolStatistics.incrementNumConnectionsClosedDefunct();
2127 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2128 null);
2129 connection.terminate(null);
2130
2131 if (closed)
2132 {
2133 throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get());
2134 }
2135
2136 return createConnection();
2137 }
2138
2139
2140
2141 /**
2142 * {@inheritDoc}
2143 */
2144 @Override()
2145 public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections()
2146 {
2147 return retryOperationTypes.get();
2148 }
2149
2150
2151
2152 /**
2153 * {@inheritDoc}
2154 */
2155 @Override()
2156 public void setRetryFailedOperationsDueToInvalidConnections(
2157 final Set<OperationType> operationTypes)
2158 {
2159 if ((operationTypes == null) || operationTypes.isEmpty())
2160 {
2161 retryOperationTypes.set(
2162 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
2163 }
2164 else
2165 {
2166 final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class);
2167 s.addAll(operationTypes);
2168 retryOperationTypes.set(Collections.unmodifiableSet(s));
2169 }
2170 }
2171
2172
2173
2174 /**
2175 * Indicates whether the provided connection should be considered expired.
2176 *
2177 * @param connection The connection for which to make the determination.
2178 *
2179 * @return {@code true} if the provided connection should be considered
2180 * expired, or {@code false} if not.
2181 */
2182 private boolean connectionIsExpired(final LDAPConnection connection)
2183 {
2184 // There may be a custom maximum connection age for the connection. If that
2185 // is the case, then use that custom max age rather than the pool-default
2186 // max age.
2187 final long maxAge;
2188 final Object maxAgeObj =
2189 connection.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE);
2190 if ((maxAgeObj != null) && (maxAgeObj instanceof Long))
2191 {
2192 maxAge = (Long) maxAgeObj;
2193 }
2194 else
2195 {
2196 maxAge = maxConnectionAge;
2197 }
2198
2199 // If connection expiration is not enabled, then there is nothing to do.
2200 if (maxAge <= 0L)
2201 {
2202 return false;
2203 }
2204
2205 // If there is a minimum disconnect interval, then make sure that we have
2206 // not closed another expired connection too recently.
2207 final long currentTime = System.currentTimeMillis();
2208 if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval)
2209 {
2210 return false;
2211 }
2212
2213 // Get the age of the connection and see if it is expired.
2214 final long connectionAge = currentTime - connection.getConnectTime();
2215 return (connectionAge > maxAge);
2216 }
2217
2218
2219
2220 /**
2221 * {@inheritDoc}
2222 */
2223 @Override()
2224 public String getConnectionPoolName()
2225 {
2226 return connectionPoolName;
2227 }
2228
2229
2230
2231 /**
2232 * {@inheritDoc}
2233 */
2234 @Override()
2235 public void setConnectionPoolName(final String connectionPoolName)
2236 {
2237 this.connectionPoolName = connectionPoolName;
2238 for (final LDAPConnection c : availableConnections)
2239 {
2240 c.setConnectionPoolName(connectionPoolName);
2241 }
2242 }
2243
2244
2245
2246 /**
2247 * Indicates whether the connection pool should create a new connection if one
2248 * is requested when there are none available.
2249 *
2250 * @return {@code true} if a new connection should be created if none are
2251 * available when a request is received, or {@code false} if an
2252 * exception should be thrown to indicate that no connection is
2253 * available.
2254 */
2255 public boolean getCreateIfNecessary()
2256 {
2257 return createIfNecessary;
2258 }
2259
2260
2261
2262 /**
2263 * Specifies whether the connection pool should create a new connection if one
2264 * is requested when there are none available.
2265 *
2266 * @param createIfNecessary Specifies whether the connection pool should
2267 * create a new connection if one is requested when
2268 * there are none available.
2269 */
2270 public void setCreateIfNecessary(final boolean createIfNecessary)
2271 {
2272 this.createIfNecessary = createIfNecessary;
2273 }
2274
2275
2276
2277 /**
2278 * Retrieves the maximum length of time in milliseconds to wait for a
2279 * connection to become available when trying to obtain a connection from the
2280 * pool.
2281 *
2282 * @return The maximum length of time in milliseconds to wait for a
2283 * connection to become available when trying to obtain a connection
2284 * from the pool, or zero to indicate that the pool should not block
2285 * at all if no connections are available and that it should either
2286 * create a new connection or throw an exception.
2287 */
2288 public long getMaxWaitTimeMillis()
2289 {
2290 return maxWaitTime;
2291 }
2292
2293
2294
2295 /**
2296 * Specifies the maximum length of time in milliseconds to wait for a
2297 * connection to become available when trying to obtain a connection from the
2298 * pool.
2299 *
2300 * @param maxWaitTime The maximum length of time in milliseconds to wait for
2301 * a connection to become available when trying to obtain
2302 * a connection from the pool. A value of zero should be
2303 * used to indicate that the pool should not block at all
2304 * if no connections are available and that it should
2305 * either create a new connection or throw an exception.
2306 */
2307 public void setMaxWaitTimeMillis(final long maxWaitTime)
2308 {
2309 if (maxWaitTime > 0L)
2310 {
2311 this.maxWaitTime = maxWaitTime;
2312 }
2313 else
2314 {
2315 this.maxWaitTime = 0L;
2316 }
2317 }
2318
2319
2320
2321 /**
2322 * Retrieves the maximum length of time in milliseconds that a connection in
2323 * this pool may be established before it is closed and replaced with another
2324 * connection.
2325 *
2326 * @return The maximum length of time in milliseconds that a connection in
2327 * this pool may be established before it is closed and replaced with
2328 * another connection, or {@code 0L} if no maximum age should be
2329 * enforced.
2330 */
2331 public long getMaxConnectionAgeMillis()
2332 {
2333 return maxConnectionAge;
2334 }
2335
2336
2337
2338 /**
2339 * Specifies the maximum length of time in milliseconds that a connection in
2340 * this pool may be established before it should be closed and replaced with
2341 * another connection.
2342 *
2343 * @param maxConnectionAge The maximum length of time in milliseconds that a
2344 * connection in this pool may be established before
2345 * it should be closed and replaced with another
2346 * connection. A value of zero indicates that no
2347 * maximum age should be enforced.
2348 */
2349 public void setMaxConnectionAgeMillis(final long maxConnectionAge)
2350 {
2351 if (maxConnectionAge > 0L)
2352 {
2353 this.maxConnectionAge = maxConnectionAge;
2354 }
2355 else
2356 {
2357 this.maxConnectionAge = 0L;
2358 }
2359 }
2360
2361
2362
2363 /**
2364 * Retrieves the maximum connection age that should be used for connections
2365 * that were created in order to replace defunct connections. It is possible
2366 * to define a custom maximum connection age for these connections to allow
2367 * them to be closed and re-established more quickly to allow for a
2368 * potentially quicker fail-back to a normal state. Note, that if this
2369 * capability is to be used, then the maximum age for these connections should
2370 * be long enough to allow the problematic server to become available again
2371 * under normal circumstances (e.g., it should be long enough for at least a
2372 * shutdown and restart of the server, plus some overhead for potentially
2373 * performing routine maintenance while the server is offline, or a chance for
2374 * an administrator to be made available that a server has gone down).
2375 *
2376 * @return The maximum connection age that should be used for connections
2377 * that were created in order to replace defunct connections, a value
2378 * of zero to indicate that no maximum age should be enforced, or
2379 * {@code null} if the value returned by the
2380 * {@link #getMaxConnectionAgeMillis()} method should be used.
2381 */
2382 public Long getMaxDefunctReplacementConnectionAgeMillis()
2383 {
2384 return maxDefunctReplacementConnectionAge;
2385 }
2386
2387
2388
2389 /**
2390 * Specifies the maximum connection age that should be used for connections
2391 * that were created in order to replace defunct connections. It is possible
2392 * to define a custom maximum connection age for these connections to allow
2393 * them to be closed and re-established more quickly to allow for a
2394 * potentially quicker fail-back to a normal state. Note, that if this
2395 * capability is to be used, then the maximum age for these connections should
2396 * be long enough to allow the problematic server to become available again
2397 * under normal circumstances (e.g., it should be long enough for at least a
2398 * shutdown and restart of the server, plus some overhead for potentially
2399 * performing routine maintenance while the server is offline, or a chance for
2400 * an administrator to be made available that a server has gone down).
2401 *
2402 * @param maxDefunctReplacementConnectionAge The maximum connection age that
2403 * should be used for connections that were created in order to
2404 * replace defunct connections. It may be zero if no maximum age
2405 * should be enforced for such connections, or it may be
2406 * {@code null} if the value returned by the
2407 * {@link #getMaxConnectionAgeMillis()} method should be used.
2408 */
2409 public void setMaxDefunctReplacementConnectionAgeMillis(
2410 final Long maxDefunctReplacementConnectionAge)
2411 {
2412 if (maxDefunctReplacementConnectionAge == null)
2413 {
2414 this.maxDefunctReplacementConnectionAge = null;
2415 }
2416 else if (maxDefunctReplacementConnectionAge > 0L)
2417 {
2418 this.maxDefunctReplacementConnectionAge =
2419 maxDefunctReplacementConnectionAge;
2420 }
2421 else
2422 {
2423 this.maxDefunctReplacementConnectionAge = 0L;
2424 }
2425 }
2426
2427
2428
2429 /**
2430 * Indicates whether to check the age of a connection against the configured
2431 * maximum connection age whenever it is released to the pool. By default,
2432 * connection age is evaluated in the background using the health check
2433 * thread, but it is also possible to configure the pool to additionally
2434 * examine the age of a connection when it is returned to the pool.
2435 * <BR><BR>
2436 * Performing connection age evaluation only in the background will ensure
2437 * that connections are only closed and re-established in a single-threaded
2438 * manner, which helps minimize the load against the target server, but only
2439 * checks connections that are not in use when the health check thread is
2440 * active. If the pool is configured to also evaluate the connection age when
2441 * connections are returned to the pool, then it may help ensure that the
2442 * maximum connection age is honored more strictly for all connections, but
2443 * in busy applications may lead to cases in which multiple connections are
2444 * closed and re-established simultaneously, which may increase load against
2445 * the directory server. The {@link #setMinDisconnectIntervalMillis(long)}
2446 * method may be used to help mitigate the potential performance impact of
2447 * closing and re-establishing multiple connections simultaneously.
2448 *
2449 * @return {@code true} if the connection pool should check connection age in
2450 * both the background health check thread and when connections are
2451 * released to the pool, or {@code false} if the connection age
2452 * should only be checked by the background health check thread.
2453 */
2454 public boolean checkConnectionAgeOnRelease()
2455 {
2456 return checkConnectionAgeOnRelease;
2457 }
2458
2459
2460
2461 /**
2462 * Specifies whether to check the age of a connection against the configured
2463 * maximum connection age whenever it is released to the pool. By default,
2464 * connection age is evaluated in the background using the health check
2465 * thread, but it is also possible to configure the pool to additionally
2466 * examine the age of a connection when it is returned to the pool.
2467 * <BR><BR>
2468 * Performing connection age evaluation only in the background will ensure
2469 * that connections are only closed and re-established in a single-threaded
2470 * manner, which helps minimize the load against the target server, but only
2471 * checks connections that are not in use when the health check thread is
2472 * active. If the pool is configured to also evaluate the connection age when
2473 * connections are returned to the pool, then it may help ensure that the
2474 * maximum connection age is honored more strictly for all connections, but
2475 * in busy applications may lead to cases in which multiple connections are
2476 * closed and re-established simultaneously, which may increase load against
2477 * the directory server. The {@link #setMinDisconnectIntervalMillis(long)}
2478 * method may be used to help mitigate the potential performance impact of
2479 * closing and re-establishing multiple connections simultaneously.
2480 *
2481 * @param checkConnectionAgeOnRelease If {@code true}, this indicates that
2482 * the connection pool should check
2483 * connection age in both the background
2484 * health check thread and when
2485 * connections are released to the pool.
2486 * If {@code false}, this indicates that
2487 * the connection pool should check
2488 * connection age only in the background
2489 * health check thread.
2490 */
2491 public void setCheckConnectionAgeOnRelease(
2492 final boolean checkConnectionAgeOnRelease)
2493 {
2494 this.checkConnectionAgeOnRelease = checkConnectionAgeOnRelease;
2495 }
2496
2497
2498
2499 /**
2500 * Retrieves the minimum length of time in milliseconds that should pass
2501 * between connections closed because they have been established for longer
2502 * than the maximum connection age.
2503 *
2504 * @return The minimum length of time in milliseconds that should pass
2505 * between connections closed because they have been established for
2506 * longer than the maximum connection age, or {@code 0L} if expired
2507 * connections may be closed as quickly as they are identified.
2508 */
2509 public long getMinDisconnectIntervalMillis()
2510 {
2511 return minDisconnectInterval;
2512 }
2513
2514
2515
2516 /**
2517 * Specifies the minimum length of time in milliseconds that should pass
2518 * between connections closed because they have been established for longer
2519 * than the maximum connection age.
2520 *
2521 * @param minDisconnectInterval The minimum length of time in milliseconds
2522 * that should pass between connections closed
2523 * because they have been established for
2524 * longer than the maximum connection age. A
2525 * value less than or equal to zero indicates
2526 * that no minimum time should be enforced.
2527 */
2528 public void setMinDisconnectIntervalMillis(final long minDisconnectInterval)
2529 {
2530 if (minDisconnectInterval > 0)
2531 {
2532 this.minDisconnectInterval = minDisconnectInterval;
2533 }
2534 else
2535 {
2536 this.minDisconnectInterval = 0L;
2537 }
2538 }
2539
2540
2541
2542 /**
2543 * {@inheritDoc}
2544 */
2545 @Override()
2546 public LDAPConnectionPoolHealthCheck getHealthCheck()
2547 {
2548 return healthCheck;
2549 }
2550
2551
2552
2553 /**
2554 * Sets the health check implementation for this connection pool.
2555 *
2556 * @param healthCheck The health check implementation for this connection
2557 * pool. It must not be {@code null}.
2558 */
2559 public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck)
2560 {
2561 ensureNotNull(healthCheck);
2562 this.healthCheck = healthCheck;
2563 }
2564
2565
2566
2567 /**
2568 * {@inheritDoc}
2569 */
2570 @Override()
2571 public long getHealthCheckIntervalMillis()
2572 {
2573 return healthCheckInterval;
2574 }
2575
2576
2577
2578 /**
2579 * {@inheritDoc}
2580 */
2581 @Override()
2582 public void setHealthCheckIntervalMillis(final long healthCheckInterval)
2583 {
2584 ensureTrue(healthCheckInterval > 0L,
2585 "LDAPConnectionPool.healthCheckInterval must be greater than 0.");
2586 this.healthCheckInterval = healthCheckInterval;
2587 healthCheckThread.wakeUp();
2588 }
2589
2590
2591
2592 /**
2593 * Indicates whether health check processing for connections operating in
2594 * synchronous mode should include attempting to perform a read from each
2595 * connection with a very short timeout. This can help detect unsolicited
2596 * responses and unexpected connection closures in a more timely manner. This
2597 * will be ignored for connections not operating in synchronous mode.
2598 *
2599 * @return {@code true} if health check processing for connections operating
2600 * in synchronous mode should include a read attempt with a very
2601 * short timeout, or {@code false} if not.
2602 */
2603 public boolean trySynchronousReadDuringHealthCheck()
2604 {
2605 return trySynchronousReadDuringHealthCheck;
2606 }
2607
2608
2609
2610 /**
2611 * Specifies whether health check processing for connections operating in
2612 * synchronous mode should include attempting to perform a read from each
2613 * connection with a very short timeout.
2614 *
2615 * @param trySynchronousReadDuringHealthCheck Indicates whether health check
2616 * processing for connections
2617 * operating in synchronous mode
2618 * should include attempting to
2619 * perform a read from each
2620 * connection with a very short
2621 * timeout.
2622 */
2623 public void setTrySynchronousReadDuringHealthCheck(
2624 final boolean trySynchronousReadDuringHealthCheck)
2625 {
2626 this.trySynchronousReadDuringHealthCheck =
2627 trySynchronousReadDuringHealthCheck;
2628 }
2629
2630
2631
2632 /**
2633 * {@inheritDoc}
2634 */
2635 @Override()
2636 protected void doHealthCheck()
2637 {
2638 invokeHealthCheck(null, true);
2639 }
2640
2641
2642
2643 /**
2644 * Invokes a synchronous one-time health-check against the connections in this
2645 * pool that are not currently in use. This will be independent of any
2646 * background health checking that may be automatically performed by the pool.
2647 *
2648 * @param healthCheck The health check to use. If this is
2649 * {@code null}, then the pool's
2650 * currently-configured health check (if any) will
2651 * be used. If this is {@code null} and there is
2652 * no health check configured for the pool, then
2653 * only a basic set of checks.
2654 * @param checkForExpiration Indicates whether to check to see if any
2655 * connections have been established for longer
2656 * than the maximum connection age. If this is
2657 * {@code true} then any expired connections will
2658 * be closed and replaced with newly-established
2659 * connections.
2660 *
2661 * @return An object with information about the result of the health check
2662 * processing.
2663 */
2664 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2665 final LDAPConnectionPoolHealthCheck healthCheck,
2666 final boolean checkForExpiration)
2667 {
2668 return invokeHealthCheck(healthCheck, checkForExpiration,
2669 checkForExpiration);
2670 }
2671
2672
2673
2674 /**
2675 * Invokes a synchronous one-time health-check against the connections in this
2676 * pool that are not currently in use. This will be independent of any
2677 * background health checking that may be automatically performed by the pool.
2678 *
2679 * @param healthCheck The health check to use. If this is
2680 * {@code null}, then the pool's
2681 * currently-configured health check (if any)
2682 * will be used. If this is {@code null} and
2683 * there is no health check configured for the
2684 * pool, then only a basic set of checks.
2685 * @param checkForExpiration Indicates whether to check to see if any
2686 * connections have been established for
2687 * longer than the maximum connection age. If
2688 * this is {@code true} then any expired
2689 * connections will be closed and replaced
2690 * with newly-established connections.
2691 * @param checkMinConnectionGoal Indicates whether to check to see if the
2692 * currently-available number of connections
2693 * is less than the minimum available
2694 * connection goal. If this is {@code true}
2695 * the minimum available connection goal is
2696 * greater than zero, and the number of
2697 * currently-available connections is less
2698 * than the goal, then this method will
2699 * attempt to create enough new connections to
2700 * reach the goal.
2701 *
2702 * @return An object with information about the result of the health check
2703 * processing.
2704 */
2705 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2706 final LDAPConnectionPoolHealthCheck healthCheck,
2707 final boolean checkForExpiration,
2708 final boolean checkMinConnectionGoal)
2709 {
2710 // Determine which health check to use.
2711 final LDAPConnectionPoolHealthCheck hc;
2712 if (healthCheck == null)
2713 {
2714 hc = this.healthCheck;
2715 }
2716 else
2717 {
2718 hc = healthCheck;
2719 }
2720
2721
2722 // Create a set used to hold connections that we've already examined. If we
2723 // encounter the same connection twice, then we know that we don't need to
2724 // do any more work.
2725 final HashSet<LDAPConnection> examinedConnections =
2726 new HashSet<LDAPConnection>(numConnections);
2727 int numExamined = 0;
2728 int numDefunct = 0;
2729 int numExpired = 0;
2730
2731 for (int i=0; i < numConnections; i++)
2732 {
2733 LDAPConnection conn = availableConnections.poll();
2734 if (conn == null)
2735 {
2736 break;
2737 }
2738 else if (examinedConnections.contains(conn))
2739 {
2740 if (! availableConnections.offer(conn))
2741 {
2742 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2743 null, null);
2744 poolStatistics.incrementNumConnectionsClosedUnneeded();
2745 conn.terminate(null);
2746 }
2747 break;
2748 }
2749
2750 numExamined++;
2751 if (! conn.isConnected())
2752 {
2753 numDefunct++;
2754 poolStatistics.incrementNumConnectionsClosedDefunct();
2755 conn = handleDefunctConnection(conn);
2756 if (conn != null)
2757 {
2758 examinedConnections.add(conn);
2759 }
2760 }
2761 else
2762 {
2763 if (checkForExpiration && connectionIsExpired(conn))
2764 {
2765 numExpired++;
2766
2767 try
2768 {
2769 final LDAPConnection newConnection = createConnection();
2770 if (availableConnections.offer(newConnection))
2771 {
2772 examinedConnections.add(newConnection);
2773 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
2774 null, null);
2775 conn.terminate(null);
2776 poolStatistics.incrementNumConnectionsClosedExpired();
2777 lastExpiredDisconnectTime = System.currentTimeMillis();
2778 continue;
2779 }
2780 else
2781 {
2782 newConnection.setDisconnectInfo(
2783 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
2784 newConnection.terminate(null);
2785 poolStatistics.incrementNumConnectionsClosedUnneeded();
2786 }
2787 }
2788 catch (final LDAPException le)
2789 {
2790 debugException(le);
2791 }
2792 }
2793
2794
2795 // If the connection is operating in synchronous mode, then try to read
2796 // a message on it using an extremely short timeout. This can help
2797 // detect a connection closure or unsolicited notification in a more
2798 // timely manner than if we had to wait for the client code to try to
2799 // use the connection.
2800 if (trySynchronousReadDuringHealthCheck && conn.synchronousMode())
2801 {
2802 int previousTimeout = Integer.MIN_VALUE;
2803 Socket s = null;
2804 try
2805 {
2806 s = conn.getConnectionInternals(true).getSocket();
2807 previousTimeout = s.getSoTimeout();
2808 s.setSoTimeout(1);
2809
2810 final LDAPResponse response = conn.readResponse(0);
2811 if (response instanceof ConnectionClosedResponse)
2812 {
2813 numDefunct++;
2814 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2815 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
2816 poolStatistics.incrementNumConnectionsClosedDefunct();
2817 conn = handleDefunctConnection(conn);
2818 if (conn != null)
2819 {
2820 examinedConnections.add(conn);
2821 }
2822 continue;
2823 }
2824 else if (response instanceof ExtendedResult)
2825 {
2826 // This means we got an unsolicited response. It could be a
2827 // notice of disconnection, or it could be something else, but in
2828 // any case we'll send it to the connection's unsolicited
2829 // notification handler (if one is defined).
2830 final UnsolicitedNotificationHandler h = conn.
2831 getConnectionOptions().getUnsolicitedNotificationHandler();
2832 if (h != null)
2833 {
2834 h.handleUnsolicitedNotification(conn,
2835 (ExtendedResult) response);
2836 }
2837 }
2838 else if (response instanceof LDAPResult)
2839 {
2840 final LDAPResult r = (LDAPResult) response;
2841 if (r.getResultCode() == ResultCode.SERVER_DOWN)
2842 {
2843 numDefunct++;
2844 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2845 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
2846 poolStatistics.incrementNumConnectionsClosedDefunct();
2847 conn = handleDefunctConnection(conn);
2848 if (conn != null)
2849 {
2850 examinedConnections.add(conn);
2851 }
2852 continue;
2853 }
2854 }
2855 }
2856 catch (final LDAPException le)
2857 {
2858 if (le.getResultCode() == ResultCode.TIMEOUT)
2859 {
2860 debugException(Level.FINEST, le);
2861 }
2862 else
2863 {
2864 debugException(le);
2865 numDefunct++;
2866 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2867 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(
2868 getExceptionMessage(le)), le);
2869 poolStatistics.incrementNumConnectionsClosedDefunct();
2870 conn = handleDefunctConnection(conn);
2871 if (conn != null)
2872 {
2873 examinedConnections.add(conn);
2874 }
2875 continue;
2876 }
2877 }
2878 catch (final Exception e)
2879 {
2880 debugException(e);
2881 numDefunct++;
2882 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2883 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(getExceptionMessage(e)),
2884 e);
2885 poolStatistics.incrementNumConnectionsClosedDefunct();
2886 conn = handleDefunctConnection(conn);
2887 if (conn != null)
2888 {
2889 examinedConnections.add(conn);
2890 }
2891 continue;
2892 }
2893 finally
2894 {
2895 if (previousTimeout != Integer.MIN_VALUE)
2896 {
2897 try
2898 {
2899 if (s != null)
2900 {
2901 s.setSoTimeout(previousTimeout);
2902 }
2903 }
2904 catch (final Exception e)
2905 {
2906 debugException(e);
2907 numDefunct++;
2908 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2909 null, e);
2910 poolStatistics.incrementNumConnectionsClosedDefunct();
2911 conn = handleDefunctConnection(conn);
2912 if (conn != null)
2913 {
2914 examinedConnections.add(conn);
2915 }
2916 continue;
2917 }
2918 }
2919 }
2920 }
2921
2922 try
2923 {
2924 hc.ensureConnectionValidForContinuedUse(conn);
2925 if (availableConnections.offer(conn))
2926 {
2927 examinedConnections.add(conn);
2928 }
2929 else
2930 {
2931 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2932 null, null);
2933 poolStatistics.incrementNumConnectionsClosedUnneeded();
2934 conn.terminate(null);
2935 }
2936 }
2937 catch (Exception e)
2938 {
2939 debugException(e);
2940 numDefunct++;
2941 poolStatistics.incrementNumConnectionsClosedDefunct();
2942 conn = handleDefunctConnection(conn);
2943 if (conn != null)
2944 {
2945 examinedConnections.add(conn);
2946 }
2947 }
2948 }
2949 }
2950
2951 if (checkMinConnectionGoal)
2952 {
2953 try
2954 {
2955 final int neededConnections =
2956 minConnectionGoal - availableConnections.size();
2957 for (int i=0; i < neededConnections; i++)
2958 {
2959 final LDAPConnection conn = createConnection(hc);
2960 if (! availableConnections.offer(conn))
2961 {
2962 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2963 null, null);
2964 poolStatistics.incrementNumConnectionsClosedUnneeded();
2965 conn.terminate(null);
2966 break;
2967 }
2968 }
2969 }
2970 catch (final Exception e)
2971 {
2972 debugException(e);
2973 }
2974 }
2975
2976 return new LDAPConnectionPoolHealthCheckResult(numExamined, numExpired,
2977 numDefunct);
2978 }
2979
2980
2981
2982 /**
2983 * {@inheritDoc}
2984 */
2985 @Override()
2986 public int getCurrentAvailableConnections()
2987 {
2988 return availableConnections.size();
2989 }
2990
2991
2992
2993 /**
2994 * {@inheritDoc}
2995 */
2996 @Override()
2997 public int getMaximumAvailableConnections()
2998 {
2999 return numConnections;
3000 }
3001
3002
3003
3004 /**
3005 * Retrieves the goal for the minimum number of available connections that the
3006 * pool should try to maintain for immediate use. If this goal is greater
3007 * than zero, then the health checking process will attempt to create enough
3008 * new connections to achieve this goal.
3009 *
3010 * @return The goal for the minimum number of available connections that the
3011 * pool should try to maintain for immediate use, or zero if it will
3012 * not try to maintain a minimum number of available connections.
3013 */
3014 public int getMinimumAvailableConnectionGoal()
3015 {
3016 return minConnectionGoal;
3017 }
3018
3019
3020
3021 /**
3022 * Specifies the goal for the minimum number of available connections that the
3023 * pool should try to maintain for immediate use. If this goal is greater
3024 * than zero, then the health checking process will attempt to create enough
3025 * new connections to achieve this goal.
3026 *
3027 * @param goal The goal for the minimum number of available connections that
3028 * the pool should try to maintain for immediate use. A value
3029 * less than or equal to zero indicates that the pool should not
3030 * try to maintain a minimum number of available connections.
3031 */
3032 public void setMinimumAvailableConnectionGoal(final int goal)
3033 {
3034 if (goal > numConnections)
3035 {
3036 minConnectionGoal = numConnections;
3037 }
3038 else if (goal > 0)
3039 {
3040 minConnectionGoal = goal;
3041 }
3042 else
3043 {
3044 minConnectionGoal = 0;
3045 }
3046 }
3047
3048
3049
3050 /**
3051 * {@inheritDoc}
3052 */
3053 @Override()
3054 public LDAPConnectionPoolStatistics getConnectionPoolStatistics()
3055 {
3056 return poolStatistics;
3057 }
3058
3059
3060
3061 /**
3062 * Attempts to reduce the number of connections available for use in the pool.
3063 * Note that this will be a best-effort attempt to reach the desired number
3064 * of connections, as other threads interacting with the connection pool may
3065 * check out and/or release connections that cause the number of available
3066 * connections to fluctuate.
3067 *
3068 * @param connectionsToRetain The number of connections that should be
3069 * retained for use in the connection pool.
3070 */
3071 public void shrinkPool(final int connectionsToRetain)
3072 {
3073 while (availableConnections.size() > connectionsToRetain)
3074 {
3075 final LDAPConnection conn;
3076 try
3077 {
3078 conn = getConnection();
3079 }
3080 catch (final LDAPException le)
3081 {
3082 return;
3083 }
3084
3085 if (availableConnections.size() >= connectionsToRetain)
3086 {
3087 discardConnection(conn);
3088 }
3089 else
3090 {
3091 releaseConnection(conn);
3092 return;
3093 }
3094 }
3095 }
3096
3097
3098
3099 /**
3100 * Closes this connection pool in the event that it becomes unreferenced.
3101 *
3102 * @throws Throwable If an unexpected problem occurs.
3103 */
3104 @Override()
3105 protected void finalize()
3106 throws Throwable
3107 {
3108 super.finalize();
3109
3110 close();
3111 }
3112
3113
3114
3115 /**
3116 * {@inheritDoc}
3117 */
3118 @Override()
3119 public void toString(final StringBuilder buffer)
3120 {
3121 buffer.append("LDAPConnectionPool(");
3122
3123 final String name = connectionPoolName;
3124 if (name != null)
3125 {
3126 buffer.append("name='");
3127 buffer.append(name);
3128 buffer.append("', ");
3129 }
3130
3131 buffer.append("serverSet=");
3132 serverSet.toString(buffer);
3133 buffer.append(", maxConnections=");
3134 buffer.append(numConnections);
3135 buffer.append(')');
3136 }
3137 }