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