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