001/*
002 * Copyright 2007-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2019 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    numConnections = maxConnections;
1169    availableConnections = new LinkedBlockingQueue<>(numConnections);
1170
1171    if (healthCheck == null)
1172    {
1173      this.healthCheck = new LDAPConnectionPoolHealthCheck();
1174    }
1175    else
1176    {
1177      this.healthCheck = healthCheck;
1178    }
1179
1180    final List<LDAPConnection> connList;
1181    if (initialConnectThreads > 1)
1182    {
1183      connList = Collections.synchronizedList(
1184           new ArrayList<LDAPConnection>(initialConnections));
1185      final ParallelPoolConnector connector = new ParallelPoolConnector(this,
1186           connList, initialConnections, initialConnectThreads,
1187           throwOnConnectFailure);
1188      connector.establishConnections();
1189    }
1190    else
1191    {
1192      connList = new ArrayList<>(initialConnections);
1193      for (int i=0; i < initialConnections; i++)
1194      {
1195        try
1196        {
1197          connList.add(createConnection());
1198        }
1199        catch (final LDAPException le)
1200        {
1201          Debug.debugException(le);
1202
1203          if (throwOnConnectFailure)
1204          {
1205            for (final LDAPConnection c : connList)
1206            {
1207              try
1208              {
1209                c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null,
1210                     le);
1211                c.setClosed();
1212              } catch (final Exception e)
1213              {
1214                Debug.debugException(e);
1215              }
1216            }
1217
1218            throw le;
1219          }
1220        }
1221      }
1222    }
1223
1224    availableConnections.addAll(connList);
1225
1226    failedReplaceCount                 =
1227         new AtomicInteger(maxConnections - availableConnections.size());
1228    createIfNecessary                  = true;
1229    checkConnectionAgeOnRelease        = false;
1230    maxConnectionAge                   = 0L;
1231    maxDefunctReplacementConnectionAge = null;
1232    minDisconnectInterval              = 0L;
1233    lastExpiredDisconnectTime          = 0L;
1234    maxWaitTime                        = 0L;
1235    closed                             = false;
1236
1237    healthCheckThread = new LDAPConnectionPoolHealthCheckThread(this);
1238    healthCheckThread.start();
1239  }
1240
1241
1242
1243  /**
1244   * Creates a new LDAP connection for use in this pool.
1245   *
1246   * @return  A new connection created for use in this pool.
1247   *
1248   * @throws  LDAPException  If a problem occurs while attempting to establish
1249   *                         the connection.  If a connection had been created,
1250   *                         it will be closed.
1251   */
1252  @SuppressWarnings("deprecation")
1253  LDAPConnection createConnection()
1254                 throws LDAPException
1255  {
1256    return createConnection(healthCheck);
1257  }
1258
1259
1260
1261  /**
1262   * Creates a new LDAP connection for use in this pool.
1263   *
1264   * @param  healthCheck  The health check to use to determine whether the
1265   *                      newly-created connection is valid.  It may be
1266   *                      {@code null} if no additional health checking should
1267   *                      be performed for the newly-created connection.
1268   *
1269   * @return  A new connection created for use in this pool.
1270   *
1271   * @throws  LDAPException  If a problem occurs while attempting to establish
1272   *                         the connection.  If a connection had been created,
1273   *                         it will be closed.
1274   */
1275  @SuppressWarnings("deprecation")
1276  private LDAPConnection createConnection(
1277                              final LDAPConnectionPoolHealthCheck healthCheck)
1278          throws LDAPException
1279  {
1280    final LDAPConnection c;
1281    try
1282    {
1283      c = serverSet.getConnection(healthCheck);
1284    }
1285    catch (final LDAPException le)
1286    {
1287      Debug.debugException(le);
1288      poolStatistics.incrementNumFailedConnectionAttempts();
1289      Debug.debugConnectionPool(Level.SEVERE, this, null,
1290           "Unable to create a new pooled connection", le);
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          Debug.debugConnectionPool(Level.SEVERE, this, c,
1322               "Exception in pre-authentication post-connect processing", e);
1323          c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1324          c.setClosed();
1325        }
1326        catch (final Exception e2)
1327        {
1328          Debug.debugException(e2);
1329        }
1330
1331        if (e instanceof LDAPException)
1332        {
1333          throw ((LDAPException) e);
1334        }
1335        else
1336        {
1337          throw new LDAPException(ResultCode.CONNECT_ERROR,
1338               ERR_POOL_POST_CONNECT_ERROR.get(
1339                    StaticUtils.getExceptionMessage(e)),
1340               e);
1341        }
1342      }
1343    }
1344
1345
1346    // Authenticate the connection if appropriate.
1347    if ((bindRequest != null) && (! serverSet.includesAuthentication()))
1348    {
1349      BindResult bindResult;
1350      try
1351      {
1352        bindResult = c.bind(bindRequest.duplicate());
1353      }
1354      catch (final LDAPBindException lbe)
1355      {
1356        Debug.debugException(lbe);
1357        bindResult = lbe.getBindResult();
1358      }
1359      catch (final LDAPException le)
1360      {
1361        Debug.debugException(le);
1362        bindResult = new BindResult(le);
1363      }
1364
1365      try
1366      {
1367        if (healthCheck != null)
1368        {
1369          healthCheck.ensureConnectionValidAfterAuthentication(c, bindResult);
1370        }
1371
1372        if (bindResult.getResultCode() != ResultCode.SUCCESS)
1373        {
1374          throw new LDAPBindException(bindResult);
1375        }
1376      }
1377      catch (final LDAPException le)
1378      {
1379        Debug.debugException(le);
1380
1381        try
1382        {
1383          poolStatistics.incrementNumFailedConnectionAttempts();
1384          if (bindResult.getResultCode() != ResultCode.SUCCESS)
1385          {
1386            Debug.debugConnectionPool(Level.SEVERE, this, c,
1387                 "Failed to authenticate a new pooled connection", le);
1388          }
1389          else
1390          {
1391            Debug.debugConnectionPool(Level.SEVERE, this, c,
1392                 "A new pooled connection failed its post-authentication " +
1393                      "health check",
1394                 le);
1395          }
1396          c.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
1397          c.setClosed();
1398        }
1399        catch (final Exception e)
1400        {
1401          Debug.debugException(e);
1402        }
1403
1404        throw le;
1405      }
1406    }
1407
1408
1409    // Invoke post-authentication post-connect processing.
1410    if (postConnectProcessor != null)
1411    {
1412      try
1413      {
1414        postConnectProcessor.processPostAuthenticatedConnection(c);
1415      }
1416      catch (final Exception e)
1417      {
1418        Debug.debugException(e);
1419        try
1420        {
1421          poolStatistics.incrementNumFailedConnectionAttempts();
1422          Debug.debugConnectionPool(Level.SEVERE, this, c,
1423               "Exception in post-authentication post-connect processing", e);
1424          c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1425          c.setClosed();
1426        }
1427        catch (final Exception e2)
1428        {
1429          Debug.debugException(e2);
1430        }
1431
1432        if (e instanceof LDAPException)
1433        {
1434          throw ((LDAPException) e);
1435        }
1436        else
1437        {
1438          throw new LDAPException(ResultCode.CONNECT_ERROR,
1439               ERR_POOL_POST_CONNECT_ERROR.get(
1440                    StaticUtils.getExceptionMessage(e)),
1441               e);
1442        }
1443      }
1444    }
1445
1446
1447    // Get the pooled schema if appropriate.
1448    if (opts.usePooledSchema())
1449    {
1450      final long currentTime = System.currentTimeMillis();
1451      if ((pooledSchema == null) || (currentTime > pooledSchema.getFirst()))
1452      {
1453        try
1454        {
1455          final Schema schema = c.getSchema();
1456          if (schema != null)
1457          {
1458            c.setCachedSchema(schema);
1459
1460            final long timeout = opts.getPooledSchemaTimeoutMillis();
1461            if ((timeout <= 0L) || (currentTime + timeout <= 0L))
1462            {
1463              pooledSchema = new ObjectPair<>(Long.MAX_VALUE, schema);
1464            }
1465            else
1466            {
1467              pooledSchema = new ObjectPair<>((currentTime+timeout), schema);
1468            }
1469          }
1470        }
1471        catch (final Exception e)
1472        {
1473          Debug.debugException(e);
1474
1475          // There was a problem retrieving the schema from the server, but if
1476          // we have an earlier copy then we can assume it's still valid.
1477          if (pooledSchema != null)
1478          {
1479            c.setCachedSchema(pooledSchema.getSecond());
1480          }
1481        }
1482      }
1483      else
1484      {
1485        c.setCachedSchema(pooledSchema.getSecond());
1486      }
1487    }
1488
1489
1490    // Finish setting up the connection.
1491    c.setConnectionPoolName(connectionPoolName);
1492    poolStatistics.incrementNumSuccessfulConnectionAttempts();
1493    Debug.debugConnectionPool(Level.INFO, this, c,
1494         "Successfully created a new pooled connection", null);
1495
1496    return c;
1497  }
1498
1499
1500
1501  /**
1502   * {@inheritDoc}
1503   */
1504  @Override()
1505  public void close()
1506  {
1507    close(true, 1);
1508  }
1509
1510
1511
1512  /**
1513   * {@inheritDoc}
1514   */
1515  @Override()
1516  public void close(final boolean unbind, final int numThreads)
1517  {
1518    try
1519    {
1520      final boolean healthCheckThreadAlreadySignaled = closed;
1521      closed = true;
1522      healthCheckThread.stopRunning(! healthCheckThreadAlreadySignaled);
1523
1524      if (numThreads > 1)
1525      {
1526        final ArrayList<LDAPConnection> connList =
1527             new ArrayList<>(availableConnections.size());
1528        availableConnections.drainTo(connList);
1529
1530        if (! connList.isEmpty())
1531        {
1532          final ParallelPoolCloser closer =
1533               new ParallelPoolCloser(connList, unbind, numThreads);
1534          closer.closeConnections();
1535        }
1536      }
1537      else
1538      {
1539        while (true)
1540        {
1541          final LDAPConnection conn = availableConnections.poll();
1542          if (conn == null)
1543          {
1544            return;
1545          }
1546          else
1547          {
1548            poolStatistics.incrementNumConnectionsClosedUnneeded();
1549            Debug.debugConnectionPool(Level.INFO, this, conn,
1550                 "Closed a connection as part of closing the connection pool",
1551                 null);
1552            conn.setDisconnectInfo(DisconnectType.POOL_CLOSED, null, null);
1553            if (unbind)
1554            {
1555              conn.terminate(null);
1556            }
1557            else
1558            {
1559              conn.setClosed();
1560            }
1561          }
1562        }
1563      }
1564    }
1565    finally
1566    {
1567      Debug.debugConnectionPool(Level.INFO, this, null,
1568           "Closed the connection pool", null);
1569    }
1570  }
1571
1572
1573
1574  /**
1575   * {@inheritDoc}
1576   */
1577  @Override()
1578  public boolean isClosed()
1579  {
1580    return closed;
1581  }
1582
1583
1584
1585  /**
1586   * Processes a simple bind using a connection from this connection pool, and
1587   * then reverts that authentication by re-binding as the same user used to
1588   * authenticate new connections.  If new connections are unauthenticated, then
1589   * the subsequent bind will be an anonymous simple bind.  This method attempts
1590   * to ensure that processing the provided bind operation does not have a
1591   * lasting impact the authentication state of the connection used to process
1592   * it.
1593   * <BR><BR>
1594   * If the second bind attempt (the one used to restore the authentication
1595   * identity) fails, the connection will be closed as defunct so that a new
1596   * connection will be created to take its place.
1597   *
1598   * @param  bindDN    The bind DN for the simple bind request.
1599   * @param  password  The password for the simple bind request.
1600   * @param  controls  The optional set of controls for the simple bind request.
1601   *
1602   * @return  The result of processing the provided bind operation.
1603   *
1604   * @throws  LDAPException  If the server rejects the bind request, or if a
1605   *                         problem occurs while sending the request or reading
1606   *                         the response.
1607   */
1608  public BindResult bindAndRevertAuthentication(final String bindDN,
1609                                                final String password,
1610                                                final Control... controls)
1611         throws LDAPException
1612  {
1613    return bindAndRevertAuthentication(
1614         new SimpleBindRequest(bindDN, password, controls));
1615  }
1616
1617
1618
1619  /**
1620   * Processes the provided bind request using a connection from this connection
1621   * pool, and then reverts that authentication by re-binding as the same user
1622   * used to authenticate new connections.  If new connections are
1623   * unauthenticated, then the subsequent bind will be an anonymous simple bind.
1624   * This method attempts to ensure that processing the provided bind operation
1625   * does not have a lasting impact the authentication state of the connection
1626   * used to process it.
1627   * <BR><BR>
1628   * If the second bind attempt (the one used to restore the authentication
1629   * identity) fails, the connection will be closed as defunct so that a new
1630   * connection will be created to take its place.
1631   *
1632   * @param  bindRequest  The bind request to be processed.  It must not be
1633   *                      {@code null}.
1634   *
1635   * @return  The result of processing the provided bind operation.
1636   *
1637   * @throws  LDAPException  If the server rejects the bind request, or if a
1638   *                         problem occurs while sending the request or reading
1639   *                         the response.
1640   */
1641  public BindResult bindAndRevertAuthentication(final BindRequest bindRequest)
1642         throws LDAPException
1643  {
1644    LDAPConnection conn = getConnection();
1645
1646    try
1647    {
1648      final BindResult result = conn.bind(bindRequest);
1649      releaseAndReAuthenticateConnection(conn);
1650      return result;
1651    }
1652    catch (final Throwable t)
1653    {
1654      Debug.debugException(t);
1655
1656      if (t instanceof LDAPException)
1657      {
1658        final LDAPException le = (LDAPException) t;
1659
1660        boolean shouldThrow;
1661        try
1662        {
1663          healthCheck.ensureConnectionValidAfterException(conn, le);
1664
1665          // The above call will throw an exception if the connection doesn't
1666          // seem to be valid, so if we've gotten here then we should assume
1667          // that it is valid and we will pass the exception onto the client
1668          // without retrying the operation.
1669          releaseAndReAuthenticateConnection(conn);
1670          shouldThrow = true;
1671        }
1672        catch (final Exception e)
1673        {
1674          Debug.debugException(e);
1675
1676          // This implies that the connection is not valid.  If the pool is
1677          // configured to re-try bind operations on a newly-established
1678          // connection, then that will be done later in this method.
1679          // Otherwise, release the connection as defunct and pass the bind
1680          // exception onto the client.
1681          if (! getOperationTypesToRetryDueToInvalidConnections().contains(
1682                     OperationType.BIND))
1683          {
1684            releaseDefunctConnection(conn);
1685            shouldThrow = true;
1686          }
1687          else
1688          {
1689            shouldThrow = false;
1690          }
1691        }
1692
1693        if (shouldThrow)
1694        {
1695          throw le;
1696        }
1697      }
1698      else
1699      {
1700        releaseDefunctConnection(conn);
1701        StaticUtils.rethrowIfError(t);
1702        throw new LDAPException(ResultCode.LOCAL_ERROR,
1703             ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t);
1704      }
1705    }
1706
1707
1708    // If we've gotten here, then the bind operation should be re-tried on a
1709    // newly-established connection.
1710    conn = replaceDefunctConnection(conn);
1711
1712    try
1713    {
1714      final BindResult result = conn.bind(bindRequest);
1715      releaseAndReAuthenticateConnection(conn);
1716      return result;
1717    }
1718    catch (final Throwable t)
1719    {
1720      Debug.debugException(t);
1721
1722      if (t instanceof LDAPException)
1723      {
1724        final LDAPException le = (LDAPException) t;
1725
1726        try
1727        {
1728          healthCheck.ensureConnectionValidAfterException(conn, le);
1729          releaseAndReAuthenticateConnection(conn);
1730        }
1731        catch (final Exception e)
1732        {
1733          Debug.debugException(e);
1734          releaseDefunctConnection(conn);
1735        }
1736
1737        throw le;
1738      }
1739      else
1740      {
1741        releaseDefunctConnection(conn);
1742        StaticUtils.rethrowIfError(t);
1743        throw new LDAPException(ResultCode.LOCAL_ERROR,
1744             ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t);
1745      }
1746    }
1747  }
1748
1749
1750
1751  /**
1752   * {@inheritDoc}
1753   */
1754  @Override()
1755  public LDAPConnection getConnection()
1756         throws LDAPException
1757  {
1758    if (closed)
1759    {
1760      poolStatistics.incrementNumFailedCheckouts();
1761      Debug.debugConnectionPool(Level.SEVERE, this, null,
1762           "Failed to get a connection to a closed connection pool", null);
1763      throw new LDAPException(ResultCode.CONNECT_ERROR,
1764                              ERR_POOL_CLOSED.get());
1765    }
1766
1767    LDAPConnection conn = availableConnections.poll();
1768    if (conn != null)
1769    {
1770      Exception connException = null;
1771      if (conn.isConnected())
1772      {
1773        try
1774        {
1775          healthCheck.ensureConnectionValidForCheckout(conn);
1776          poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1777          Debug.debugConnectionPool(Level.INFO, this, conn,
1778               "Checked out an immediately available pooled connection", null);
1779          return conn;
1780        }
1781        catch (final LDAPException le)
1782        {
1783          Debug.debugException(le);
1784          connException = le;
1785        }
1786      }
1787
1788      poolStatistics.incrementNumConnectionsClosedDefunct();
1789      Debug.debugConnectionPool(Level.WARNING, this, conn,
1790           "Closing a defunct connection encountered during checkout",
1791           connException);
1792      handleDefunctConnection(conn);
1793      for (int i=0; i < numConnections; i++)
1794      {
1795        conn = availableConnections.poll();
1796        if (conn == null)
1797        {
1798          break;
1799        }
1800        else if (conn.isConnected())
1801        {
1802          try
1803          {
1804            healthCheck.ensureConnectionValidForCheckout(conn);
1805            poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1806            Debug.debugConnectionPool(Level.INFO, this, conn,
1807                 "Checked out an immediately available pooled connection",
1808                 null);
1809            return conn;
1810          }
1811          catch (final LDAPException le)
1812          {
1813            Debug.debugException(le);
1814            poolStatistics.incrementNumConnectionsClosedDefunct();
1815            Debug.debugConnectionPool(Level.WARNING, this, conn,
1816                 "Closing a defunct connection encountered during checkout",
1817                 le);
1818            handleDefunctConnection(conn);
1819          }
1820        }
1821        else
1822        {
1823          poolStatistics.incrementNumConnectionsClosedDefunct();
1824          Debug.debugConnectionPool(Level.WARNING, this, conn,
1825               "Closing a defunct connection encountered during checkout",
1826               null);
1827          handleDefunctConnection(conn);
1828        }
1829      }
1830    }
1831
1832    if (failedReplaceCount.get() > 0)
1833    {
1834      final int newReplaceCount = failedReplaceCount.getAndDecrement();
1835      if (newReplaceCount > 0)
1836      {
1837        try
1838        {
1839          conn = createConnection();
1840          poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1841          Debug.debugConnectionPool(Level.INFO, this, conn,
1842               "Checked out a newly created connection", null);
1843          return conn;
1844        }
1845        catch (final LDAPException le)
1846        {
1847          Debug.debugException(le);
1848          failedReplaceCount.incrementAndGet();
1849          poolStatistics.incrementNumFailedCheckouts();
1850          Debug.debugConnectionPool(Level.SEVERE, this, conn,
1851               "Unable to create a new connection for checkout", le);
1852          throw le;
1853        }
1854      }
1855      else
1856      {
1857        failedReplaceCount.incrementAndGet();
1858      }
1859    }
1860
1861    if (maxWaitTime > 0)
1862    {
1863      try
1864      {
1865        final long startWaitTime = System.currentTimeMillis();
1866        conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS);
1867        final long elapsedWaitTime = System.currentTimeMillis() - startWaitTime;
1868        if (conn != null)
1869        {
1870          try
1871          {
1872            healthCheck.ensureConnectionValidForCheckout(conn);
1873            poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting();
1874            Debug.debugConnectionPool(Level.INFO, this, conn,
1875                 "Checked out an existing connection after waiting " +
1876                      elapsedWaitTime + "ms for it to become available",
1877                 null);
1878            return conn;
1879          }
1880          catch (final LDAPException le)
1881          {
1882            Debug.debugException(le);
1883            poolStatistics.incrementNumConnectionsClosedDefunct();
1884            Debug.debugConnectionPool(Level.WARNING, this, conn,
1885                 "Got a connection for checkout after waiting " +
1886                      elapsedWaitTime + "ms for it to become available, but " +
1887                      "the connection failed the checkout health check",
1888                 le);
1889            handleDefunctConnection(conn);
1890          }
1891        }
1892      }
1893      catch (final InterruptedException ie)
1894      {
1895        Debug.debugException(ie);
1896        Thread.currentThread().interrupt();
1897        throw new LDAPException(ResultCode.LOCAL_ERROR,
1898             ERR_POOL_CHECKOUT_INTERRUPTED.get(), ie);
1899      }
1900    }
1901
1902    if (createIfNecessary)
1903    {
1904      try
1905      {
1906        conn = createConnection();
1907        poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1908        Debug.debugConnectionPool(Level.INFO, this, conn,
1909             "Checked out a newly created connection", null);
1910        return conn;
1911      }
1912      catch (final LDAPException le)
1913      {
1914        Debug.debugException(le);
1915        poolStatistics.incrementNumFailedCheckouts();
1916        Debug.debugConnectionPool(Level.SEVERE, this, null,
1917             "Unable to create a new connection for checkout", le);
1918        throw le;
1919      }
1920    }
1921    else
1922    {
1923      poolStatistics.incrementNumFailedCheckouts();
1924      Debug.debugConnectionPool(Level.SEVERE, this, null,
1925           "Unable to check out a connection because none are available",
1926           null);
1927      throw new LDAPException(ResultCode.CONNECT_ERROR,
1928                              ERR_POOL_NO_CONNECTIONS.get());
1929    }
1930  }
1931
1932
1933
1934  /**
1935   * Attempts to retrieve a connection from the pool that is established to the
1936   * specified server.  Note that this method will only attempt to return an
1937   * existing connection that is currently available, and will not create a
1938   * connection or wait for any checked-out connections to be returned.
1939   *
1940   * @param  host  The address of the server to which the desired connection
1941   *               should be established.  This must not be {@code null}, and
1942   *               this must exactly match the address provided for the initial
1943   *               connection or the {@code ServerSet} used to create the pool.
1944   * @param  port  The port of the server to which the desired connection should
1945   *               be established.
1946   *
1947   * @return  A connection that is established to the specified server, or
1948   *          {@code null} if there are no available connections established to
1949   *          the specified server.
1950   */
1951  public LDAPConnection getConnection(final String host, final int port)
1952  {
1953    if (closed)
1954    {
1955      poolStatistics.incrementNumFailedCheckouts();
1956      Debug.debugConnectionPool(Level.WARNING, this, null,
1957           "Failed to get a connection to a closed connection pool", null);
1958      return null;
1959    }
1960
1961    final HashSet<LDAPConnection> examinedConnections =
1962         new HashSet<>(StaticUtils.computeMapCapacity(numConnections));
1963    while (true)
1964    {
1965      final LDAPConnection conn = availableConnections.poll();
1966      if (conn == null)
1967      {
1968        poolStatistics.incrementNumFailedCheckouts();
1969        Debug.debugConnectionPool(Level.SEVERE, this, null,
1970             "Failed to get an existing connection to " + host + ':' + port +
1971                  " because no connections are immediately available",
1972             null);
1973        return null;
1974      }
1975
1976      if (examinedConnections.contains(conn))
1977      {
1978        if (! availableConnections.offer(conn))
1979        {
1980          discardConnection(conn);
1981        }
1982
1983        poolStatistics.incrementNumFailedCheckouts();
1984        Debug.debugConnectionPool(Level.WARNING, this, null,
1985             "Failed to get an existing connection to " + host + ':' + port +
1986                  " because none of the available connections are " +
1987                  "established to that server",
1988             null);
1989        return null;
1990      }
1991
1992      if (conn.getConnectedAddress().equals(host) &&
1993          (port == conn.getConnectedPort()))
1994      {
1995        try
1996        {
1997          healthCheck.ensureConnectionValidForCheckout(conn);
1998          poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1999          Debug.debugConnectionPool(Level.INFO, this, conn,
2000               "Successfully checked out an existing connection to requested " +
2001                    "server " + host + ':' + port,
2002               null);
2003          return conn;
2004        }
2005        catch (final LDAPException le)
2006        {
2007          Debug.debugException(le);
2008          poolStatistics.incrementNumConnectionsClosedDefunct();
2009          Debug.debugConnectionPool(Level.WARNING, this, conn,
2010               "Closing an existing connection to requested server " + host +
2011                    ':' + port + " because it failed the checkout health " +
2012                    "check",
2013               le);
2014          handleDefunctConnection(conn);
2015          continue;
2016        }
2017      }
2018
2019      if (availableConnections.offer(conn))
2020      {
2021        examinedConnections.add(conn);
2022      }
2023      else
2024      {
2025        discardConnection(conn);
2026      }
2027    }
2028  }
2029
2030
2031
2032  /**
2033   * {@inheritDoc}
2034   */
2035  @Override()
2036  public void releaseConnection(final LDAPConnection connection)
2037  {
2038    if (connection == null)
2039    {
2040      return;
2041    }
2042
2043    connection.setConnectionPoolName(connectionPoolName);
2044    if (checkConnectionAgeOnRelease && connectionIsExpired(connection))
2045    {
2046      try
2047      {
2048        final LDAPConnection newConnection = createConnection();
2049        if (availableConnections.offer(newConnection))
2050        {
2051          connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
2052               null, null);
2053          connection.terminate(null);
2054          poolStatistics.incrementNumConnectionsClosedExpired();
2055          Debug.debugConnectionPool(Level.WARNING, this, connection,
2056               "Closing a released connection because it is expired", null);
2057          lastExpiredDisconnectTime = System.currentTimeMillis();
2058        }
2059        else
2060        {
2061          newConnection.setDisconnectInfo(
2062               DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
2063          newConnection.terminate(null);
2064          poolStatistics.incrementNumConnectionsClosedUnneeded();
2065          Debug.debugConnectionPool(Level.WARNING, this, connection,
2066               "Closing a released connection because the pool is already full",
2067               null);
2068        }
2069      }
2070      catch (final LDAPException le)
2071      {
2072        Debug.debugException(le);
2073      }
2074      return;
2075    }
2076
2077    try
2078    {
2079      healthCheck.ensureConnectionValidForRelease(connection);
2080    }
2081    catch (final LDAPException le)
2082    {
2083      releaseDefunctConnection(connection);
2084      return;
2085    }
2086
2087    if (availableConnections.offer(connection))
2088    {
2089      poolStatistics.incrementNumReleasedValid();
2090      Debug.debugConnectionPool(Level.INFO, this, connection,
2091           "Released a connection back to the pool", null);
2092    }
2093    else
2094    {
2095      // This means that the connection pool is full, which can happen if the
2096      // pool was empty when a request came in to retrieve a connection and
2097      // createIfNecessary was true.  In this case, we'll just close the
2098      // connection since we don't need it any more.
2099      connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2100                                   null, null);
2101      poolStatistics.incrementNumConnectionsClosedUnneeded();
2102      Debug.debugConnectionPool(Level.WARNING, this, connection,
2103           "Closing a released connection because the pool is already full",
2104           null);
2105      connection.terminate(null);
2106      return;
2107    }
2108
2109    if (closed)
2110    {
2111      close();
2112    }
2113  }
2114
2115
2116
2117  /**
2118   * Indicates that the provided connection should be removed from the pool,
2119   * and that no new connection should be created to take its place.  This may
2120   * be used to shrink the pool if such functionality is desired.
2121   *
2122   * @param  connection  The connection to be discarded.
2123   */
2124  public void discardConnection(final LDAPConnection connection)
2125  {
2126    if (connection == null)
2127    {
2128      return;
2129    }
2130
2131    connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2132         null, null);
2133    connection.terminate(null);
2134    poolStatistics.incrementNumConnectionsClosedUnneeded();
2135    Debug.debugConnectionPool(Level.INFO, this, connection,
2136         "Discareded a connection that is no longer needed", null);
2137
2138    if (availableConnections.remainingCapacity() > 0)
2139    {
2140      final int newReplaceCount = failedReplaceCount.incrementAndGet();
2141      if (newReplaceCount > numConnections)
2142      {
2143        failedReplaceCount.set(numConnections);
2144      }
2145    }
2146  }
2147
2148
2149
2150  /**
2151   * Performs a bind on the provided connection before releasing it back to the
2152   * pool, so that it will be authenticated as the same user as
2153   * newly-established connections.  If newly-established connections are
2154   * unauthenticated, then this method will perform an anonymous simple bind to
2155   * ensure that the resulting connection is unauthenticated.
2156   *
2157   * Releases the provided connection back to this pool.
2158   *
2159   * @param  connection  The connection to be released back to the pool after
2160   *                     being re-authenticated.
2161   */
2162  public void releaseAndReAuthenticateConnection(
2163                   final LDAPConnection connection)
2164  {
2165    if (connection == null)
2166    {
2167      return;
2168    }
2169
2170    try
2171    {
2172      BindResult bindResult;
2173      try
2174      {
2175        if (bindRequest == null)
2176        {
2177          bindResult = connection.bind("", "");
2178        }
2179        else
2180        {
2181          bindResult = connection.bind(bindRequest.duplicate());
2182        }
2183      }
2184      catch (final LDAPBindException lbe)
2185      {
2186        Debug.debugException(lbe);
2187        bindResult = lbe.getBindResult();
2188      }
2189
2190      try
2191      {
2192        healthCheck.ensureConnectionValidAfterAuthentication(connection,
2193             bindResult);
2194        if (bindResult.getResultCode() != ResultCode.SUCCESS)
2195        {
2196          throw new LDAPBindException(bindResult);
2197        }
2198      }
2199      catch (final LDAPException le)
2200      {
2201        Debug.debugException(le);
2202
2203        try
2204        {
2205          connection.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
2206          connection.setClosed();
2207          releaseDefunctConnection(connection);
2208        }
2209        catch (final Exception e)
2210        {
2211          Debug.debugException(e);
2212        }
2213
2214        throw le;
2215      }
2216
2217      releaseConnection(connection);
2218    }
2219    catch (final Exception e)
2220    {
2221      Debug.debugException(e);
2222      releaseDefunctConnection(connection);
2223    }
2224  }
2225
2226
2227
2228  /**
2229   * {@inheritDoc}
2230   */
2231  @Override()
2232  public void releaseDefunctConnection(final LDAPConnection connection)
2233  {
2234    if (connection == null)
2235    {
2236      return;
2237    }
2238
2239    connection.setConnectionPoolName(connectionPoolName);
2240    poolStatistics.incrementNumConnectionsClosedDefunct();
2241    Debug.debugConnectionPool(Level.WARNING, this, connection,
2242         "Releasing a defunct connection", null);
2243    handleDefunctConnection(connection);
2244  }
2245
2246
2247
2248  /**
2249   * Performs the real work of terminating a defunct connection and replacing it
2250   * with a new connection if possible.
2251   *
2252   * @param  connection  The defunct connection to be replaced.
2253   *
2254   * @return  The new connection created to take the place of the defunct
2255   *          connection, or {@code null} if no new connection was created.
2256   *          Note that if a connection is returned, it will have already been
2257   *          made available and the caller must not rely on it being unused for
2258   *          any other purpose.
2259   */
2260  private LDAPConnection handleDefunctConnection(
2261                              final LDAPConnection connection)
2262  {
2263    connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2264                                 null);
2265    connection.setClosed();
2266
2267    if (closed)
2268    {
2269      return null;
2270    }
2271
2272    if (createIfNecessary && (availableConnections.remainingCapacity() <= 0))
2273    {
2274      return null;
2275    }
2276
2277    try
2278    {
2279      final LDAPConnection conn = createConnection();
2280      if (maxDefunctReplacementConnectionAge != null)
2281      {
2282        // Only set the maximum age if there isn't one already set for the
2283        // connection (i.e., because it was defined by the server set).
2284        if (conn.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE) == null)
2285        {
2286          conn.setAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE,
2287               maxDefunctReplacementConnectionAge);
2288        }
2289      }
2290
2291      if (! availableConnections.offer(conn))
2292      {
2293        conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2294                               null, null);
2295        conn.terminate(null);
2296        return null;
2297      }
2298
2299      return conn;
2300    }
2301    catch (final LDAPException le)
2302    {
2303      Debug.debugException(le);
2304      final int newReplaceCount = failedReplaceCount.incrementAndGet();
2305      if (newReplaceCount > numConnections)
2306      {
2307        failedReplaceCount.set(numConnections);
2308      }
2309      return null;
2310    }
2311  }
2312
2313
2314
2315  /**
2316   * {@inheritDoc}
2317   */
2318  @Override()
2319  public LDAPConnection replaceDefunctConnection(
2320                             final LDAPConnection connection)
2321         throws LDAPException
2322  {
2323    poolStatistics.incrementNumConnectionsClosedDefunct();
2324    Debug.debugConnectionPool(Level.WARNING, this, connection,
2325         "Releasing a defunct connection that is to be replaced", null);
2326    connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2327                                 null);
2328    connection.setClosed();
2329
2330    if (closed)
2331    {
2332      throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get());
2333    }
2334
2335    try
2336    {
2337      return createConnection();
2338    }
2339    catch (final LDAPException le)
2340    {
2341      Debug.debugException(le);
2342      failedReplaceCount.incrementAndGet();
2343      throw le;
2344    }
2345  }
2346
2347
2348
2349  /**
2350   * {@inheritDoc}
2351   */
2352  @Override()
2353  public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections()
2354  {
2355    return retryOperationTypes.get();
2356  }
2357
2358
2359
2360  /**
2361   * {@inheritDoc}
2362   */
2363  @Override()
2364  public void setRetryFailedOperationsDueToInvalidConnections(
2365                   final Set<OperationType> operationTypes)
2366  {
2367    if ((operationTypes == null) || operationTypes.isEmpty())
2368    {
2369      retryOperationTypes.set(
2370           Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
2371    }
2372    else
2373    {
2374      final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class);
2375      s.addAll(operationTypes);
2376      retryOperationTypes.set(Collections.unmodifiableSet(s));
2377    }
2378  }
2379
2380
2381
2382  /**
2383   * Indicates whether the provided connection should be considered expired.
2384   *
2385   * @param  connection  The connection for which to make the determination.
2386   *
2387   * @return  {@code true} if the provided connection should be considered
2388   *          expired, or {@code false} if not.
2389   */
2390  private boolean connectionIsExpired(final LDAPConnection connection)
2391  {
2392    // There may be a custom maximum connection age for the connection.  If that
2393    // is the case, then use that custom max age rather than the pool-default
2394    // max age.
2395    final long maxAge;
2396    final Object maxAgeObj =
2397         connection.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE);
2398    if ((maxAgeObj != null) && (maxAgeObj instanceof Long))
2399    {
2400      maxAge = (Long) maxAgeObj;
2401    }
2402    else
2403    {
2404      maxAge = maxConnectionAge;
2405    }
2406
2407    // If connection expiration is not enabled, then there is nothing to do.
2408    if (maxAge <= 0L)
2409    {
2410      return false;
2411    }
2412
2413    // If there is a minimum disconnect interval, then make sure that we have
2414    // not closed another expired connection too recently.
2415    final long currentTime = System.currentTimeMillis();
2416    if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval)
2417    {
2418      return false;
2419    }
2420
2421    // Get the age of the connection and see if it is expired.
2422    final long connectionAge = currentTime - connection.getConnectTime();
2423    return (connectionAge > maxAge);
2424  }
2425
2426
2427
2428  /**
2429   * Specifies the bind request that will be used to authenticate subsequent new
2430   * connections that are established by this connection pool.  The
2431   * authentication state for existing connections will not be altered unless
2432   * one of the {@code bindAndRevertAuthentication} or
2433   * {@code releaseAndReAuthenticateConnection} methods are invoked on those
2434   * connections.
2435   *
2436   * @param  bindRequest  The bind request that will be used to authenticate new
2437   *                      connections that are established by this pool, or
2438   *                      that will be applied to existing connections via the
2439   *                      {@code bindAndRevertAuthentication} or
2440   *                      {@code releaseAndReAuthenticateConnection} method.  It
2441   *                      may be {@code null} if new connections should be
2442   *                      unauthenticated.
2443   */
2444  public void setBindRequest(final BindRequest bindRequest)
2445  {
2446    this.bindRequest = bindRequest;
2447  }
2448
2449
2450
2451  /**
2452   * Specifies the server set that should be used to establish new connections
2453   * for use in this connection pool.  Existing connections will not be
2454   * affected.
2455   *
2456   * @param  serverSet  The server set that should be used to establish new
2457   *                    connections for use in this connection pool.  It must
2458   *                    not be {@code null}.
2459   */
2460  public void setServerSet(final ServerSet serverSet)
2461  {
2462    Validator.ensureNotNull(serverSet);
2463    this.serverSet = serverSet;
2464  }
2465
2466
2467
2468  /**
2469   * {@inheritDoc}
2470   */
2471  @Override()
2472  public String getConnectionPoolName()
2473  {
2474    return connectionPoolName;
2475  }
2476
2477
2478
2479  /**
2480   * {@inheritDoc}
2481   */
2482  @Override()
2483  public void setConnectionPoolName(final String connectionPoolName)
2484  {
2485    this.connectionPoolName = connectionPoolName;
2486    for (final LDAPConnection c : availableConnections)
2487    {
2488      c.setConnectionPoolName(connectionPoolName);
2489    }
2490  }
2491
2492
2493
2494  /**
2495   * Indicates whether the connection pool should create a new connection if one
2496   * is requested when there are none available.
2497   *
2498   * @return  {@code true} if a new connection should be created if none are
2499   *          available when a request is received, or {@code false} if an
2500   *          exception should be thrown to indicate that no connection is
2501   *          available.
2502   */
2503  public boolean getCreateIfNecessary()
2504  {
2505    return createIfNecessary;
2506  }
2507
2508
2509
2510  /**
2511   * Specifies whether the connection pool should create a new connection if one
2512   * is requested when there are none available.
2513   *
2514   * @param  createIfNecessary  Specifies whether the connection pool should
2515   *                            create a new connection if one is requested when
2516   *                            there are none available.
2517   */
2518  public void setCreateIfNecessary(final boolean createIfNecessary)
2519  {
2520    this.createIfNecessary = createIfNecessary;
2521  }
2522
2523
2524
2525  /**
2526   * Retrieves the maximum length of time in milliseconds to wait for a
2527   * connection to become available when trying to obtain a connection from the
2528   * pool.
2529   *
2530   * @return  The maximum length of time in milliseconds to wait for a
2531   *          connection to become available when trying to obtain a connection
2532   *          from the pool, or zero to indicate that the pool should not block
2533   *          at all if no connections are available and that it should either
2534   *          create a new connection or throw an exception.
2535   */
2536  public long getMaxWaitTimeMillis()
2537  {
2538    return maxWaitTime;
2539  }
2540
2541
2542
2543  /**
2544   * Specifies the maximum length of time in milliseconds to wait for a
2545   * connection to become available when trying to obtain a connection from the
2546   * pool.
2547   *
2548   * @param  maxWaitTime  The maximum length of time in milliseconds to wait for
2549   *                      a connection to become available when trying to obtain
2550   *                      a connection from the pool.  A value of zero should be
2551   *                      used to indicate that the pool should not block at all
2552   *                      if no connections are available and that it should
2553   *                      either create a new connection or throw an exception.
2554   */
2555  public void setMaxWaitTimeMillis(final long maxWaitTime)
2556  {
2557    if (maxWaitTime > 0L)
2558    {
2559      this.maxWaitTime = maxWaitTime;
2560    }
2561    else
2562    {
2563      this.maxWaitTime = 0L;
2564    }
2565  }
2566
2567
2568
2569  /**
2570   * Retrieves the maximum length of time in milliseconds that a connection in
2571   * this pool may be established before it is closed and replaced with another
2572   * connection.
2573   *
2574   * @return  The maximum length of time in milliseconds that a connection in
2575   *          this pool may be established before it is closed and replaced with
2576   *          another connection, or {@code 0L} if no maximum age should be
2577   *          enforced.
2578   */
2579  public long getMaxConnectionAgeMillis()
2580  {
2581    return maxConnectionAge;
2582  }
2583
2584
2585
2586  /**
2587   * Specifies the maximum length of time in milliseconds that a connection in
2588   * this pool may be established before it should be closed and replaced with
2589   * another connection.
2590   *
2591   * @param  maxConnectionAge  The maximum length of time in milliseconds that a
2592   *                           connection in this pool may be established before
2593   *                           it should be closed and replaced with another
2594   *                           connection.  A value of zero indicates that no
2595   *                           maximum age should be enforced.
2596   */
2597  public void setMaxConnectionAgeMillis(final long maxConnectionAge)
2598  {
2599    if (maxConnectionAge > 0L)
2600    {
2601      this.maxConnectionAge = maxConnectionAge;
2602    }
2603    else
2604    {
2605      this.maxConnectionAge = 0L;
2606    }
2607  }
2608
2609
2610
2611  /**
2612   * Retrieves the maximum connection age that should be used for connections
2613   * that were created in order to replace defunct connections.  It is possible
2614   * to define a custom maximum connection age for these connections to allow
2615   * them to be closed and re-established more quickly to allow for a
2616   * potentially quicker fail-back to a normal state.  Note, that if this
2617   * capability is to be used, then the maximum age for these connections should
2618   * be long enough to allow the problematic server to become available again
2619   * under normal circumstances (e.g., it should be long enough for at least a
2620   * shutdown and restart of the server, plus some overhead for potentially
2621   * performing routine maintenance while the server is offline, or a chance for
2622   * an administrator to be made available that a server has gone down).
2623   *
2624   * @return  The maximum connection age that should be used for connections
2625   *          that were created in order to replace defunct connections, a value
2626   *          of zero to indicate that no maximum age should be enforced, or
2627   *          {@code null} if the value returned by the
2628   *          {@link #getMaxConnectionAgeMillis()} method should be used.
2629   */
2630  public Long getMaxDefunctReplacementConnectionAgeMillis()
2631  {
2632    return maxDefunctReplacementConnectionAge;
2633  }
2634
2635
2636
2637  /**
2638   * Specifies the maximum connection age that should be used for connections
2639   * that were created in order to replace defunct connections.  It is possible
2640   * to define a custom maximum connection age for these connections to allow
2641   * them to be closed and re-established more quickly to allow for a
2642   * potentially quicker fail-back to a normal state.  Note, that if this
2643   * capability is to be used, then the maximum age for these connections should
2644   * be long enough to allow the problematic server to become available again
2645   * under normal circumstances (e.g., it should be long enough for at least a
2646   * shutdown and restart of the server, plus some overhead for potentially
2647   * performing routine maintenance while the server is offline, or a chance for
2648   * an administrator to be made available that a server has gone down).
2649   *
2650   * @param  maxDefunctReplacementConnectionAge  The maximum connection age that
2651   *              should be used for connections that were created in order to
2652   *              replace defunct connections.  It may be zero if no maximum age
2653   *              should be enforced for such connections, or it may be
2654   *              {@code null} if the value returned by the
2655   *              {@link #getMaxConnectionAgeMillis()} method should be used.
2656   */
2657  public void setMaxDefunctReplacementConnectionAgeMillis(
2658                   final Long maxDefunctReplacementConnectionAge)
2659  {
2660    if (maxDefunctReplacementConnectionAge == null)
2661    {
2662      this.maxDefunctReplacementConnectionAge = null;
2663    }
2664    else if (maxDefunctReplacementConnectionAge > 0L)
2665    {
2666      this.maxDefunctReplacementConnectionAge =
2667           maxDefunctReplacementConnectionAge;
2668    }
2669    else
2670    {
2671      this.maxDefunctReplacementConnectionAge = 0L;
2672    }
2673  }
2674
2675
2676
2677  /**
2678   * Indicates whether to check the age of a connection against the configured
2679   * maximum connection age whenever it is released to the pool.  By default,
2680   * connection age is evaluated in the background using the health check
2681   * thread, but it is also possible to configure the pool to additionally
2682   * examine the age of a connection when it is returned to the pool.
2683   * <BR><BR>
2684   * Performing connection age evaluation only in the background will ensure
2685   * that connections are only closed and re-established in a single-threaded
2686   * manner, which helps minimize the load against the target server, but only
2687   * checks connections that are not in use when the health check thread is
2688   * active.  If the pool is configured to also evaluate the connection age when
2689   * connections are returned to the pool, then it may help ensure that the
2690   * maximum connection age is honored more strictly for all connections, but
2691   * in busy applications may lead to cases in which multiple connections are
2692   * closed and re-established simultaneously, which may increase load against
2693   * the directory server.  The {@link #setMinDisconnectIntervalMillis(long)}
2694   * method may be used to help mitigate the potential performance impact of
2695   * closing and re-establishing multiple connections simultaneously.
2696   *
2697   * @return  {@code true} if the connection pool should check connection age in
2698   *          both the background health check thread and when connections are
2699   *          released to the pool, or {@code false} if the connection age
2700   *          should only be checked by the background health check thread.
2701   */
2702  public boolean checkConnectionAgeOnRelease()
2703  {
2704    return checkConnectionAgeOnRelease;
2705  }
2706
2707
2708
2709  /**
2710   * Specifies whether to check the age of a connection against the configured
2711   * maximum connection age whenever it is released to the pool.  By default,
2712   * connection age is evaluated in the background using the health check
2713   * thread, but it is also possible to configure the pool to additionally
2714   * examine the age of a connection when it is returned to the pool.
2715   * <BR><BR>
2716   * Performing connection age evaluation only in the background will ensure
2717   * that connections are only closed and re-established in a single-threaded
2718   * manner, which helps minimize the load against the target server, but only
2719   * checks connections that are not in use when the health check thread is
2720   * active.  If the pool is configured to also evaluate the connection age when
2721   * connections are returned to the pool, then it may help ensure that the
2722   * maximum connection age is honored more strictly for all connections, but
2723   * in busy applications may lead to cases in which multiple connections are
2724   * closed and re-established simultaneously, which may increase load against
2725   * the directory server.  The {@link #setMinDisconnectIntervalMillis(long)}
2726   * method may be used to help mitigate the potential performance impact of
2727   * closing and re-establishing multiple connections simultaneously.
2728   *
2729   * @param  checkConnectionAgeOnRelease  If {@code true}, this indicates that
2730   *                                      the connection pool should check
2731   *                                      connection age in both the background
2732   *                                      health check thread and when
2733   *                                      connections are released to the pool.
2734   *                                      If {@code false}, this indicates that
2735   *                                      the connection pool should check
2736   *                                      connection age only in the background
2737   *                                      health check thread.
2738   */
2739  public void setCheckConnectionAgeOnRelease(
2740                   final boolean checkConnectionAgeOnRelease)
2741  {
2742    this.checkConnectionAgeOnRelease = checkConnectionAgeOnRelease;
2743  }
2744
2745
2746
2747  /**
2748   * Retrieves the minimum length of time in milliseconds that should pass
2749   * between connections closed because they have been established for longer
2750   * than the maximum connection age.
2751   *
2752   * @return  The minimum length of time in milliseconds that should pass
2753   *          between connections closed because they have been established for
2754   *          longer than the maximum connection age, or {@code 0L} if expired
2755   *          connections may be closed as quickly as they are identified.
2756   */
2757  public long getMinDisconnectIntervalMillis()
2758  {
2759    return minDisconnectInterval;
2760  }
2761
2762
2763
2764  /**
2765   * Specifies the minimum length of time in milliseconds that should pass
2766   * between connections closed because they have been established for longer
2767   * than the maximum connection age.
2768   *
2769   * @param  minDisconnectInterval  The minimum length of time in milliseconds
2770   *                                that should pass between connections closed
2771   *                                because they have been established for
2772   *                                longer than the maximum connection age.  A
2773   *                                value less than or equal to zero indicates
2774   *                                that no minimum time should be enforced.
2775   */
2776  public void setMinDisconnectIntervalMillis(final long minDisconnectInterval)
2777  {
2778    if (minDisconnectInterval > 0)
2779    {
2780      this.minDisconnectInterval = minDisconnectInterval;
2781    }
2782    else
2783    {
2784      this.minDisconnectInterval = 0L;
2785    }
2786  }
2787
2788
2789
2790  /**
2791   * {@inheritDoc}
2792   */
2793  @Override()
2794  public LDAPConnectionPoolHealthCheck getHealthCheck()
2795  {
2796    return healthCheck;
2797  }
2798
2799
2800
2801  /**
2802   * Sets the health check implementation for this connection pool.
2803   *
2804   * @param  healthCheck  The health check implementation for this connection
2805   *                      pool.  It must not be {@code null}.
2806   */
2807  public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck)
2808  {
2809    Validator.ensureNotNull(healthCheck);
2810    this.healthCheck = healthCheck;
2811  }
2812
2813
2814
2815  /**
2816   * {@inheritDoc}
2817   */
2818  @Override()
2819  public long getHealthCheckIntervalMillis()
2820  {
2821    return healthCheckInterval;
2822  }
2823
2824
2825
2826  /**
2827   * {@inheritDoc}
2828   */
2829  @Override()
2830  public void setHealthCheckIntervalMillis(final long healthCheckInterval)
2831  {
2832    Validator.ensureTrue(healthCheckInterval > 0L,
2833         "LDAPConnectionPool.healthCheckInterval must be greater than 0.");
2834    this.healthCheckInterval = healthCheckInterval;
2835    healthCheckThread.wakeUp();
2836  }
2837
2838
2839
2840  /**
2841   * Indicates whether health check processing for connections operating in
2842   * synchronous mode should include attempting to perform a read from each
2843   * connection with a very short timeout.  This can help detect unsolicited
2844   * responses and unexpected connection closures in a more timely manner.  This
2845   * will be ignored for connections not operating in synchronous mode.
2846   *
2847   * @return  {@code true} if health check processing for connections operating
2848   *          in synchronous mode should include a read attempt with a very
2849   *          short timeout, or {@code false} if not.
2850   */
2851  public boolean trySynchronousReadDuringHealthCheck()
2852  {
2853    return trySynchronousReadDuringHealthCheck;
2854  }
2855
2856
2857
2858  /**
2859   * Specifies whether health check processing for connections operating in
2860   * synchronous mode should include attempting to perform a read from each
2861   * connection with a very short timeout.
2862   *
2863   * @param  trySynchronousReadDuringHealthCheck  Indicates whether health check
2864   *                                              processing for connections
2865   *                                              operating in synchronous mode
2866   *                                              should include attempting to
2867   *                                              perform a read from each
2868   *                                              connection with a very short
2869   *                                              timeout.
2870   */
2871  public void setTrySynchronousReadDuringHealthCheck(
2872                   final boolean trySynchronousReadDuringHealthCheck)
2873  {
2874    this.trySynchronousReadDuringHealthCheck =
2875         trySynchronousReadDuringHealthCheck;
2876  }
2877
2878
2879
2880  /**
2881   * {@inheritDoc}
2882   */
2883  @Override()
2884  protected void doHealthCheck()
2885  {
2886    invokeHealthCheck(null, true);
2887  }
2888
2889
2890
2891  /**
2892   * Invokes a synchronous one-time health-check against the connections in this
2893   * pool that are not currently in use.  This will be independent of any
2894   * background health checking that may be automatically performed by the pool.
2895   *
2896   * @param  healthCheck         The health check to use.  If this is
2897   *                             {@code null}, then the pool's
2898   *                             currently-configured health check (if any) will
2899   *                             be used.  If this is {@code null} and there is
2900   *                             no health check configured for the pool, then
2901   *                             only a basic set of checks.
2902   * @param  checkForExpiration  Indicates whether to check to see if any
2903   *                             connections have been established for longer
2904   *                             than the maximum connection age.  If this is
2905   *                             {@code true} then any expired connections will
2906   *                             be closed and replaced with newly-established
2907   *                             connections.
2908   *
2909   * @return  An object with information about the result of the health check
2910   *          processing.
2911   */
2912  public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2913              final LDAPConnectionPoolHealthCheck healthCheck,
2914              final boolean checkForExpiration)
2915  {
2916    return invokeHealthCheck(healthCheck, checkForExpiration,
2917         checkForExpiration);
2918  }
2919
2920
2921
2922  /**
2923   * Invokes a synchronous one-time health-check against the connections in this
2924   * pool that are not currently in use.  This will be independent of any
2925   * background health checking that may be automatically performed by the pool.
2926   *
2927   * @param  healthCheck             The health check to use.  If this is
2928   *                                 {@code null}, then the pool's
2929   *                                 currently-configured health check (if any)
2930   *                                 will be used.  If this is {@code null} and
2931   *                                 there is no health check configured for the
2932   *                                 pool, then only a basic set of checks.
2933   * @param  checkForExpiration      Indicates whether to check to see if any
2934   *                                 connections have been established for
2935   *                                 longer than the maximum connection age.  If
2936   *                                 this is {@code true} then any expired
2937   *                                 connections will be closed and replaced
2938   *                                 with newly-established connections.
2939   * @param  checkMinConnectionGoal  Indicates whether to check to see if the
2940   *                                 currently-available number of connections
2941   *                                 is less than the minimum available
2942   *                                 connection goal.  If this is {@code true}
2943   *                                 the minimum available connection goal is
2944   *                                 greater than zero, and the number of
2945   *                                 currently-available connections is less
2946   *                                 than the goal, then this method will
2947   *                                 attempt to create enough new connections to
2948   *                                 reach the goal.
2949   *
2950   * @return  An object with information about the result of the health check
2951   *          processing.
2952   */
2953  public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2954              final LDAPConnectionPoolHealthCheck healthCheck,
2955              final boolean checkForExpiration,
2956              final boolean checkMinConnectionGoal)
2957  {
2958    // Determine which health check to use.
2959    final LDAPConnectionPoolHealthCheck hc;
2960    if (healthCheck == null)
2961    {
2962      hc = this.healthCheck;
2963    }
2964    else
2965    {
2966      hc = healthCheck;
2967    }
2968
2969
2970    // Create a set used to hold connections that we've already examined.  If we
2971    // encounter the same connection twice, then we know that we don't need to
2972    // do any more work.
2973    final HashSet<LDAPConnection> examinedConnections =
2974         new HashSet<>(StaticUtils.computeMapCapacity(numConnections));
2975    int numExamined = 0;
2976    int numDefunct = 0;
2977    int numExpired = 0;
2978
2979    for (int i=0; i < numConnections; i++)
2980    {
2981      LDAPConnection conn = availableConnections.poll();
2982      if (conn == null)
2983      {
2984        break;
2985      }
2986      else if (examinedConnections.contains(conn))
2987      {
2988        if (! availableConnections.offer(conn))
2989        {
2990          conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2991                                 null, null);
2992          poolStatistics.incrementNumConnectionsClosedUnneeded();
2993          Debug.debugConnectionPool(Level.INFO, this, conn,
2994               "Closing a connection that had just been health checked " +
2995                    "because the pool is now full", null);
2996          conn.terminate(null);
2997        }
2998        break;
2999      }
3000
3001      numExamined++;
3002      if (! conn.isConnected())
3003      {
3004        numDefunct++;
3005        poolStatistics.incrementNumConnectionsClosedDefunct();
3006        Debug.debugConnectionPool(Level.WARNING, this, conn,
3007             "Closing a connection that was identified as not established " +
3008                  "during health check processing",
3009             null);
3010        conn = handleDefunctConnection(conn);
3011        if (conn != null)
3012        {
3013          examinedConnections.add(conn);
3014        }
3015      }
3016      else
3017      {
3018        if (checkForExpiration && connectionIsExpired(conn))
3019        {
3020          numExpired++;
3021
3022          try
3023          {
3024            final LDAPConnection newConnection = createConnection();
3025            if (availableConnections.offer(newConnection))
3026            {
3027              examinedConnections.add(newConnection);
3028              conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
3029                   null, null);
3030              conn.terminate(null);
3031              poolStatistics.incrementNumConnectionsClosedExpired();
3032              Debug.debugConnectionPool(Level.INFO, this, conn,
3033                   "Closing a connection that was identified as expired " +
3034                        "during health check processing",
3035                   null);
3036              lastExpiredDisconnectTime = System.currentTimeMillis();
3037              continue;
3038            }
3039            else
3040            {
3041              newConnection.setDisconnectInfo(
3042                   DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
3043              newConnection.terminate(null);
3044              poolStatistics.incrementNumConnectionsClosedUnneeded();
3045              Debug.debugConnectionPool(Level.INFO, this, newConnection,
3046                   "Closing a newly created connection created to replace " +
3047                        "an expired connection because the pool is already " +
3048                        "full",
3049                   null);
3050            }
3051          }
3052          catch (final LDAPException le)
3053          {
3054            Debug.debugException(le);
3055          }
3056        }
3057
3058
3059        // If the connection is operating in synchronous mode, then try to read
3060        // a message on it using an extremely short timeout.  This can help
3061        // detect a connection closure or unsolicited notification in a more
3062        // timely manner than if we had to wait for the client code to try to
3063        // use the connection.
3064        if (trySynchronousReadDuringHealthCheck && conn.synchronousMode())
3065        {
3066          int previousTimeout = Integer.MIN_VALUE;
3067          Socket s = null;
3068          try
3069          {
3070            s = conn.getConnectionInternals(true).getSocket();
3071            previousTimeout = s.getSoTimeout();
3072            InternalSDKHelper.setSoTimeout(conn, 1);
3073
3074            final LDAPResponse response = conn.readResponse(0);
3075            if (response instanceof ConnectionClosedResponse)
3076            {
3077              numDefunct++;
3078              conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
3079                   ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
3080              poolStatistics.incrementNumConnectionsClosedDefunct();
3081              Debug.debugConnectionPool(Level.WARNING, this, conn,
3082                   "Closing existing connection discovered to be " +
3083                        "disconnected during health check processing",
3084                   null);
3085              conn = handleDefunctConnection(conn);
3086              if (conn != null)
3087              {
3088                examinedConnections.add(conn);
3089              }
3090              continue;
3091            }
3092            else if (response instanceof ExtendedResult)
3093            {
3094              // This means we got an unsolicited response.  It could be a
3095              // notice of disconnection, or it could be something else, but in
3096              // any case we'll send it to the connection's unsolicited
3097              // notification handler (if one is defined).
3098              final UnsolicitedNotificationHandler h = conn.
3099                   getConnectionOptions().getUnsolicitedNotificationHandler();
3100              if (h != null)
3101              {
3102                h.handleUnsolicitedNotification(conn,
3103                     (ExtendedResult) response);
3104              }
3105            }
3106            else if (response instanceof LDAPResult)
3107            {
3108              final LDAPResult r = (LDAPResult) response;
3109              if (r.getResultCode() == ResultCode.SERVER_DOWN)
3110              {
3111                numDefunct++;
3112                conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
3113                     ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
3114                poolStatistics.incrementNumConnectionsClosedDefunct();
3115                Debug.debugConnectionPool(Level.WARNING, this, conn,
3116                     "Closing existing connection discovered to be invalid " +
3117                          "with result " + r + " during health check " +
3118                          "processing",
3119                     null);
3120                conn = handleDefunctConnection(conn);
3121                if (conn != null)
3122                {
3123                  examinedConnections.add(conn);
3124                }
3125                continue;
3126              }
3127            }
3128          }
3129          catch (final LDAPException le)
3130          {
3131            if (le.getResultCode() == ResultCode.TIMEOUT)
3132            {
3133              Debug.debugException(Level.FINEST, le);
3134            }
3135            else
3136            {
3137              Debug.debugException(le);
3138              numDefunct++;
3139              conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
3140                   ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(
3141                        StaticUtils.getExceptionMessage(le)), le);
3142              poolStatistics.incrementNumConnectionsClosedDefunct();
3143              Debug.debugConnectionPool(Level.WARNING, this, conn,
3144                   "Closing existing connection discovered to be invalid " +
3145                        "during health check processing",
3146                   le);
3147              conn = handleDefunctConnection(conn);
3148              if (conn != null)
3149              {
3150                examinedConnections.add(conn);
3151              }
3152              continue;
3153            }
3154          }
3155          catch (final Exception e)
3156          {
3157            Debug.debugException(e);
3158            numDefunct++;
3159            conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
3160                 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(
3161                      StaticUtils.getExceptionMessage(e)),
3162                 e);
3163            poolStatistics.incrementNumConnectionsClosedDefunct();
3164            Debug.debugConnectionPool(Level.SEVERE, this, conn,
3165                 "Closing existing connection discovered to be invalid " +
3166                      "with an unexpected exception type during health check " +
3167                      "processing",
3168                 e);
3169            conn = handleDefunctConnection(conn);
3170            if (conn != null)
3171            {
3172              examinedConnections.add(conn);
3173            }
3174            continue;
3175          }
3176          finally
3177          {
3178            if (previousTimeout != Integer.MIN_VALUE)
3179            {
3180              try
3181              {
3182                if (s != null)
3183                {
3184                  InternalSDKHelper.setSoTimeout(conn, previousTimeout);
3185                }
3186              }
3187              catch (final Exception e)
3188              {
3189                Debug.debugException(e);
3190                numDefunct++;
3191                conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
3192                     null, e);
3193                poolStatistics.incrementNumConnectionsClosedDefunct();
3194                Debug.debugConnectionPool(Level.SEVERE, this, conn,
3195                     "Closing existing connection during health check " +
3196                          "processing because an error occurred while " +
3197                          "attempting to set the SO_TIMEOUT",
3198                     e);
3199                conn = handleDefunctConnection(conn);
3200                if (conn != null)
3201                {
3202                  examinedConnections.add(conn);
3203                }
3204                continue;
3205              }
3206            }
3207          }
3208        }
3209
3210        try
3211        {
3212          hc.ensureConnectionValidForContinuedUse(conn);
3213          if (availableConnections.offer(conn))
3214          {
3215            examinedConnections.add(conn);
3216          }
3217          else
3218          {
3219            conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
3220                                   null, null);
3221            poolStatistics.incrementNumConnectionsClosedUnneeded();
3222            Debug.debugConnectionPool(Level.INFO, this, conn,
3223                 "Closing existing connection that passed health check " +
3224                      "processing because the pool is already full",
3225                 null);
3226            conn.terminate(null);
3227          }
3228        }
3229        catch (final Exception e)
3230        {
3231          Debug.debugException(e);
3232          numDefunct++;
3233          poolStatistics.incrementNumConnectionsClosedDefunct();
3234          Debug.debugConnectionPool(Level.WARNING, this, conn,
3235               "Closing existing connection that failed health check " +
3236                    "processing",
3237               e);
3238          conn = handleDefunctConnection(conn);
3239          if (conn != null)
3240          {
3241            examinedConnections.add(conn);
3242          }
3243        }
3244      }
3245    }
3246
3247    if (checkMinConnectionGoal)
3248    {
3249      try
3250      {
3251        final int neededConnections =
3252             minConnectionGoal - availableConnections.size();
3253        for (int i=0; i < neededConnections; i++)
3254        {
3255          final LDAPConnection conn = createConnection(hc);
3256          if (! availableConnections.offer(conn))
3257          {
3258            conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
3259                                   null, null);
3260            poolStatistics.incrementNumConnectionsClosedUnneeded();
3261            Debug.debugConnectionPool(Level.INFO, this, conn,
3262                 "Closing a new connection that was created during health " +
3263                      "check processing in achieve the minimum connection " +
3264                      "goal, but the pool had already become full after the " +
3265                      "connection was created",
3266                 null);
3267            conn.terminate(null);
3268            break;
3269          }
3270        }
3271      }
3272      catch (final Exception e)
3273      {
3274        Debug.debugException(e);
3275      }
3276    }
3277
3278    return new LDAPConnectionPoolHealthCheckResult(numExamined, numExpired,
3279         numDefunct);
3280  }
3281
3282
3283
3284  /**
3285   * {@inheritDoc}
3286   */
3287  @Override()
3288  public int getCurrentAvailableConnections()
3289  {
3290    return availableConnections.size();
3291  }
3292
3293
3294
3295  /**
3296   * {@inheritDoc}
3297   */
3298  @Override()
3299  public int getMaximumAvailableConnections()
3300  {
3301    return numConnections;
3302  }
3303
3304
3305
3306  /**
3307   * Retrieves the goal for the minimum number of available connections that the
3308   * pool should try to maintain for immediate use.  If this goal is greater
3309   * than zero, then the health checking process will attempt to create enough
3310   * new connections to achieve this goal.
3311   *
3312   * @return  The goal for the minimum number of available connections that the
3313   *          pool should try to maintain for immediate use, or zero if it will
3314   *          not try to maintain a minimum number of available connections.
3315   */
3316  public int getMinimumAvailableConnectionGoal()
3317  {
3318    return minConnectionGoal;
3319  }
3320
3321
3322
3323  /**
3324   * Specifies the goal for the minimum number of available connections that the
3325   * pool should try to maintain for immediate use.  If this goal is greater
3326   * than zero, then the health checking process will attempt to create enough
3327   * new connections to achieve this goal.
3328   *
3329   * @param  goal  The goal for the minimum number of available connections that
3330   *               the pool should try to maintain for immediate use.  A value
3331   *               less than or equal to zero indicates that the pool should not
3332   *               try to maintain a minimum number of available connections.
3333   */
3334  public void setMinimumAvailableConnectionGoal(final int goal)
3335  {
3336    if (goal > numConnections)
3337    {
3338      minConnectionGoal = numConnections;
3339    }
3340    else if (goal > 0)
3341    {
3342      minConnectionGoal = goal;
3343    }
3344    else
3345    {
3346      minConnectionGoal = 0;
3347    }
3348  }
3349
3350
3351
3352  /**
3353   * {@inheritDoc}
3354   */
3355  @Override()
3356  public LDAPConnectionPoolStatistics getConnectionPoolStatistics()
3357  {
3358    return poolStatistics;
3359  }
3360
3361
3362
3363  /**
3364   * Attempts to reduce the number of connections available for use in the pool.
3365   * Note that this will be a best-effort attempt to reach the desired number
3366   * of connections, as other threads interacting with the connection pool may
3367   * check out and/or release connections that cause the number of available
3368   * connections to fluctuate.
3369   *
3370   * @param  connectionsToRetain  The number of connections that should be
3371   *                              retained for use in the connection pool.
3372   */
3373  public void shrinkPool(final int connectionsToRetain)
3374  {
3375    while (availableConnections.size() > connectionsToRetain)
3376    {
3377      final LDAPConnection conn;
3378      try
3379      {
3380        conn = getConnection();
3381      }
3382      catch (final LDAPException le)
3383      {
3384        return;
3385      }
3386
3387      if (availableConnections.size() >= connectionsToRetain)
3388      {
3389        discardConnection(conn);
3390      }
3391      else
3392      {
3393        releaseConnection(conn);
3394        return;
3395      }
3396    }
3397  }
3398
3399
3400
3401  /**
3402   * Closes this connection pool in the event that it becomes unreferenced.
3403   *
3404   * @throws  Throwable  If an unexpected problem occurs.
3405   */
3406  @Override()
3407  protected void finalize()
3408            throws Throwable
3409  {
3410    super.finalize();
3411
3412    close();
3413  }
3414
3415
3416
3417  /**
3418   * {@inheritDoc}
3419   */
3420  @Override()
3421  public void toString(final StringBuilder buffer)
3422  {
3423    buffer.append("LDAPConnectionPool(");
3424
3425    final String name = connectionPoolName;
3426    if (name != null)
3427    {
3428      buffer.append("name='");
3429      buffer.append(name);
3430      buffer.append("', ");
3431    }
3432
3433    buffer.append("serverSet=");
3434    serverSet.toString(buffer);
3435    buffer.append(", maxConnections=");
3436    buffer.append(numConnections);
3437    buffer.append(')');
3438  }
3439}