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