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