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