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