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