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