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