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