001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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 = serverSet.getConnection(healthCheck);
1199        c.setConnectionPool(this);
1200    
1201        // Auto-reconnect must be disabled for pooled connections, so turn it off
1202        // if the associated connection options have it enabled for some reason.
1203        LDAPConnectionOptions opts = c.getConnectionOptions();
1204        if (opts.autoReconnect())
1205        {
1206          opts = opts.duplicate();
1207          opts.setAutoReconnect(false);
1208          c.setConnectionOptions(opts);
1209        }
1210    
1211    
1212        // Invoke pre-authentication post-connect processing.
1213        if (postConnectProcessor != null)
1214        {
1215          try
1216          {
1217            postConnectProcessor.processPreAuthenticatedConnection(c);
1218          }
1219          catch (Exception e)
1220          {
1221            debugException(e);
1222    
1223            try
1224            {
1225              poolStatistics.incrementNumFailedConnectionAttempts();
1226              c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1227              c.terminate(null);
1228            }
1229            catch (Exception e2)
1230            {
1231              debugException(e2);
1232            }
1233    
1234            if (e instanceof LDAPException)
1235            {
1236              throw ((LDAPException) e);
1237            }
1238            else
1239            {
1240              throw new LDAPException(ResultCode.CONNECT_ERROR,
1241                   ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
1242            }
1243          }
1244        }
1245    
1246    
1247        // Authenticate the connection if appropriate.
1248        BindResult bindResult = null;
1249        try
1250        {
1251          if (bindRequest != null)
1252          {
1253            bindResult = c.bind(bindRequest.duplicate());
1254          }
1255        }
1256        catch (final LDAPBindException lbe)
1257        {
1258          debugException(lbe);
1259          bindResult = lbe.getBindResult();
1260        }
1261        catch (final LDAPException le)
1262        {
1263          debugException(le);
1264          bindResult = new BindResult(le);
1265        }
1266    
1267        if (bindResult != null)
1268        {
1269          try
1270          {
1271            healthCheck.ensureConnectionValidAfterAuthentication(c, bindResult);
1272            if (bindResult.getResultCode() != ResultCode.SUCCESS)
1273            {
1274              throw new LDAPBindException(bindResult);
1275            }
1276          }
1277          catch (final LDAPException le)
1278          {
1279            debugException(le);
1280    
1281            try
1282            {
1283              poolStatistics.incrementNumFailedConnectionAttempts();
1284              c.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
1285              c.terminate(null);
1286            }
1287            catch (final Exception e)
1288            {
1289              debugException(e);
1290            }
1291    
1292            throw le;
1293          }
1294        }
1295    
1296    
1297        // Invoke post-authentication post-connect processing.
1298        if (postConnectProcessor != null)
1299        {
1300          try
1301          {
1302            postConnectProcessor.processPostAuthenticatedConnection(c);
1303          }
1304          catch (Exception e)
1305          {
1306            debugException(e);
1307            try
1308            {
1309              poolStatistics.incrementNumFailedConnectionAttempts();
1310              c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
1311              c.terminate(null);
1312            }
1313            catch (Exception e2)
1314            {
1315              debugException(e2);
1316            }
1317    
1318            if (e instanceof LDAPException)
1319            {
1320              throw ((LDAPException) e);
1321            }
1322            else
1323            {
1324              throw new LDAPException(ResultCode.CONNECT_ERROR,
1325                   ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
1326            }
1327          }
1328        }
1329    
1330    
1331        // Get the pooled schema if appropriate.
1332        if (opts.usePooledSchema())
1333        {
1334          final long currentTime = System.currentTimeMillis();
1335          if ((pooledSchema == null) || (currentTime > pooledSchema.getFirst()))
1336          {
1337            try
1338            {
1339              final Schema schema = c.getSchema();
1340              if (schema != null)
1341              {
1342                c.setCachedSchema(schema);
1343    
1344                final long timeout = opts.getPooledSchemaTimeoutMillis();
1345                if ((timeout <= 0L) || (currentTime + timeout <= 0L))
1346                {
1347                  pooledSchema =
1348                       new ObjectPair<Long,Schema>(Long.MAX_VALUE, schema);
1349                }
1350                else
1351                {
1352                  pooledSchema =
1353                       new ObjectPair<Long,Schema>((currentTime+timeout), schema);
1354                }
1355              }
1356            }
1357            catch (final Exception e)
1358            {
1359              debugException(e);
1360    
1361              // There was a problem retrieving the schema from the server, but if
1362              // we have an earlier copy then we can assume it's still valid.
1363              if (pooledSchema != null)
1364              {
1365                c.setCachedSchema(pooledSchema.getSecond());
1366              }
1367            }
1368          }
1369          else
1370          {
1371            c.setCachedSchema(pooledSchema.getSecond());
1372          }
1373        }
1374    
1375    
1376        // Finish setting up the connection.
1377        c.setConnectionPoolName(connectionPoolName);
1378        poolStatistics.incrementNumSuccessfulConnectionAttempts();
1379    
1380        return c;
1381      }
1382    
1383    
1384    
1385      /**
1386       * {@inheritDoc}
1387       */
1388      @Override()
1389      public void close()
1390      {
1391        close(true, 1);
1392      }
1393    
1394    
1395    
1396      /**
1397       * {@inheritDoc}
1398       */
1399      @Override()
1400      public void close(final boolean unbind, final int numThreads)
1401      {
1402        closed = true;
1403        healthCheckThread.stopRunning();
1404    
1405        if (numThreads > 1)
1406        {
1407          final ArrayList<LDAPConnection> connList =
1408               new ArrayList<LDAPConnection>(availableConnections.size());
1409          availableConnections.drainTo(connList);
1410    
1411          if (! connList.isEmpty())
1412          {
1413            final ParallelPoolCloser closer =
1414                 new ParallelPoolCloser(connList, unbind, numThreads);
1415            closer.closeConnections();
1416          }
1417        }
1418        else
1419        {
1420          while (true)
1421          {
1422            final LDAPConnection conn = availableConnections.poll();
1423            if (conn == null)
1424            {
1425              return;
1426            }
1427            else
1428            {
1429              poolStatistics.incrementNumConnectionsClosedUnneeded();
1430              conn.setDisconnectInfo(DisconnectType.POOL_CLOSED, null, null);
1431              if (unbind)
1432              {
1433                conn.terminate(null);
1434              }
1435              else
1436              {
1437                conn.setClosed();
1438              }
1439            }
1440          }
1441        }
1442      }
1443    
1444    
1445    
1446      /**
1447       * {@inheritDoc}
1448       */
1449      @Override()
1450      public boolean isClosed()
1451      {
1452        return closed;
1453      }
1454    
1455    
1456    
1457      /**
1458       * Processes a simple bind using a connection from this connection pool, and
1459       * then reverts that authentication by re-binding as the same user used to
1460       * authenticate new connections.  If new connections are unauthenticated, then
1461       * the subsequent bind will be an anonymous simple bind.  This method attempts
1462       * to ensure that processing the provided bind operation does not have a
1463       * lasting impact the authentication state of the connection used to process
1464       * it.
1465       * <BR><BR>
1466       * If the second bind attempt (the one used to restore the authentication
1467       * identity) fails, the connection will be closed as defunct so that a new
1468       * connection will be created to take its place.
1469       *
1470       * @param  bindDN    The bind DN for the simple bind request.
1471       * @param  password  The password for the simple bind request.
1472       * @param  controls  The optional set of controls for the simple bind request.
1473       *
1474       * @return  The result of processing the provided bind operation.
1475       *
1476       * @throws  LDAPException  If the server rejects the bind request, or if a
1477       *                         problem occurs while sending the request or reading
1478       *                         the response.
1479       */
1480      public BindResult bindAndRevertAuthentication(final String bindDN,
1481                                                    final String password,
1482                                                    final Control... controls)
1483             throws LDAPException
1484      {
1485        return bindAndRevertAuthentication(
1486             new SimpleBindRequest(bindDN, password, controls));
1487      }
1488    
1489    
1490    
1491      /**
1492       * Processes the provided bind request using a connection from this connection
1493       * pool, and then reverts that authentication by re-binding as the same user
1494       * used to authenticate new connections.  If new connections are
1495       * unauthenticated, then the subsequent bind will be an anonymous simple bind.
1496       * This method attempts to ensure that processing the provided bind operation
1497       * does not have a lasting impact the authentication state of the connection
1498       * used to process it.
1499       * <BR><BR>
1500       * If the second bind attempt (the one used to restore the authentication
1501       * identity) fails, the connection will be closed as defunct so that a new
1502       * connection will be created to take its place.
1503       *
1504       * @param  bindRequest  The bind request to be processed.  It must not be
1505       *                      {@code null}.
1506       *
1507       * @return  The result of processing the provided bind operation.
1508       *
1509       * @throws  LDAPException  If the server rejects the bind request, or if a
1510       *                         problem occurs while sending the request or reading
1511       *                         the response.
1512       */
1513      public BindResult bindAndRevertAuthentication(final BindRequest bindRequest)
1514             throws LDAPException
1515      {
1516        LDAPConnection conn = getConnection();
1517    
1518        try
1519        {
1520          final BindResult result = conn.bind(bindRequest);
1521          releaseAndReAuthenticateConnection(conn);
1522          return result;
1523        }
1524        catch (final Throwable t)
1525        {
1526          debugException(t);
1527    
1528          if (t instanceof LDAPException)
1529          {
1530            final LDAPException le = (LDAPException) t;
1531    
1532            boolean shouldThrow;
1533            try
1534            {
1535              healthCheck.ensureConnectionValidAfterException(conn, le);
1536    
1537              // The above call will throw an exception if the connection doesn't
1538              // seem to be valid, so if we've gotten here then we should assume
1539              // that it is valid and we will pass the exception onto the client
1540              // without retrying the operation.
1541              releaseAndReAuthenticateConnection(conn);
1542              shouldThrow = true;
1543            }
1544            catch (final Exception e)
1545            {
1546              debugException(e);
1547    
1548              // This implies that the connection is not valid.  If the pool is
1549              // configured to re-try bind operations on a newly-established
1550              // connection, then that will be done later in this method.
1551              // Otherwise, release the connection as defunct and pass the bind
1552              // exception onto the client.
1553              if (! getOperationTypesToRetryDueToInvalidConnections().contains(
1554                         OperationType.BIND))
1555              {
1556                releaseDefunctConnection(conn);
1557                shouldThrow = true;
1558              }
1559              else
1560              {
1561                shouldThrow = false;
1562              }
1563            }
1564    
1565            if (shouldThrow)
1566            {
1567              throw le;
1568            }
1569          }
1570          else
1571          {
1572            releaseDefunctConnection(conn);
1573            throw new LDAPException(ResultCode.LOCAL_ERROR,
1574                 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t);
1575          }
1576        }
1577    
1578    
1579        // If we've gotten here, then the bind operation should be re-tried on a
1580        // newly-established connection.
1581        conn = replaceDefunctConnection(conn);
1582    
1583        try
1584        {
1585          final BindResult result = conn.bind(bindRequest);
1586          releaseAndReAuthenticateConnection(conn);
1587          return result;
1588        }
1589        catch (final Throwable t)
1590        {
1591          debugException(t);
1592    
1593          if (t instanceof LDAPException)
1594          {
1595            final LDAPException le = (LDAPException) t;
1596    
1597            try
1598            {
1599              healthCheck.ensureConnectionValidAfterException(conn, le);
1600              releaseAndReAuthenticateConnection(conn);
1601            }
1602            catch (final Exception e)
1603            {
1604              debugException(e);
1605              releaseDefunctConnection(conn);
1606            }
1607    
1608            throw le;
1609          }
1610          else
1611          {
1612            releaseDefunctConnection(conn);
1613            throw new LDAPException(ResultCode.LOCAL_ERROR,
1614                 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t);
1615          }
1616        }
1617      }
1618    
1619    
1620    
1621      /**
1622       * {@inheritDoc}
1623       */
1624      @Override()
1625      public LDAPConnection getConnection()
1626             throws LDAPException
1627      {
1628        if (closed)
1629        {
1630          poolStatistics.incrementNumFailedCheckouts();
1631          throw new LDAPException(ResultCode.CONNECT_ERROR,
1632                                  ERR_POOL_CLOSED.get());
1633        }
1634    
1635        LDAPConnection conn = availableConnections.poll();
1636        if (conn != null)
1637        {
1638          if (conn.isConnected())
1639          {
1640            try
1641            {
1642              healthCheck.ensureConnectionValidForCheckout(conn);
1643              poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1644              return conn;
1645            }
1646            catch (LDAPException le)
1647            {
1648              debugException(le);
1649            }
1650          }
1651    
1652          poolStatistics.incrementNumConnectionsClosedDefunct();
1653          handleDefunctConnection(conn);
1654          for (int i=0; i < numConnections; i++)
1655          {
1656            conn = availableConnections.poll();
1657            if (conn == null)
1658            {
1659              break;
1660            }
1661            else if (conn.isConnected())
1662            {
1663              try
1664              {
1665                healthCheck.ensureConnectionValidForCheckout(conn);
1666                poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1667                return conn;
1668              }
1669              catch (LDAPException le)
1670              {
1671                debugException(le);
1672                poolStatistics.incrementNumConnectionsClosedDefunct();
1673                handleDefunctConnection(conn);
1674              }
1675            }
1676            else
1677            {
1678              poolStatistics.incrementNumConnectionsClosedDefunct();
1679              handleDefunctConnection(conn);
1680            }
1681          }
1682        }
1683    
1684        if (failedReplaceCount.get() > 0)
1685        {
1686          final int newReplaceCount = failedReplaceCount.getAndDecrement();
1687          if (newReplaceCount > 0)
1688          {
1689            try
1690            {
1691              conn = createConnection();
1692              poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1693              return conn;
1694            }
1695            catch (LDAPException le)
1696            {
1697              debugException(le);
1698              failedReplaceCount.incrementAndGet();
1699              poolStatistics.incrementNumFailedCheckouts();
1700              throw le;
1701            }
1702          }
1703          else
1704          {
1705            failedReplaceCount.incrementAndGet();
1706            poolStatistics.incrementNumFailedCheckouts();
1707            throw new LDAPException(ResultCode.CONNECT_ERROR,
1708                                    ERR_POOL_NO_CONNECTIONS.get());
1709          }
1710        }
1711    
1712        if (maxWaitTime > 0)
1713        {
1714          try
1715          {
1716            conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS);
1717            if (conn != null)
1718            {
1719              try
1720              {
1721                healthCheck.ensureConnectionValidForCheckout(conn);
1722                poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting();
1723                return conn;
1724              }
1725              catch (LDAPException le)
1726              {
1727                debugException(le);
1728                poolStatistics.incrementNumConnectionsClosedDefunct();
1729                handleDefunctConnection(conn);
1730              }
1731            }
1732          }
1733          catch (InterruptedException ie)
1734          {
1735            debugException(ie);
1736          }
1737        }
1738    
1739        if (createIfNecessary)
1740        {
1741          try
1742          {
1743            conn = createConnection();
1744            poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
1745            return conn;
1746          }
1747          catch (LDAPException le)
1748          {
1749            debugException(le);
1750            poolStatistics.incrementNumFailedCheckouts();
1751            throw le;
1752          }
1753        }
1754        else
1755        {
1756          poolStatistics.incrementNumFailedCheckouts();
1757          throw new LDAPException(ResultCode.CONNECT_ERROR,
1758                                  ERR_POOL_NO_CONNECTIONS.get());
1759        }
1760      }
1761    
1762    
1763    
1764      /**
1765       * Attempts to retrieve a connection from the pool that is established to the
1766       * specified server.  Note that this method will only attempt to return an
1767       * existing connection that is currently available, and will not create a
1768       * connection or wait for any checked-out connections to be returned.
1769       *
1770       * @param  host  The address of the server to which the desired connection
1771       *               should be established.  This must not be {@code null}, and
1772       *               this must exactly match the address provided for the initial
1773       *               connection or the {@code ServerSet} used to create the pool.
1774       * @param  port  The port of the server to which the desired connection should
1775       *               be established.
1776       *
1777       * @return  A connection that is established to the specified server, or
1778       *          {@code null} if there are no available connections established to
1779       *          the specified server.
1780       */
1781      public LDAPConnection getConnection(final String host, final int port)
1782      {
1783        if (closed)
1784        {
1785          poolStatistics.incrementNumFailedCheckouts();
1786          return null;
1787        }
1788    
1789        final HashSet<LDAPConnection> examinedConnections =
1790             new HashSet<LDAPConnection>(numConnections);
1791        while (true)
1792        {
1793          final LDAPConnection conn = availableConnections.poll();
1794          if (conn == null)
1795          {
1796            poolStatistics.incrementNumFailedCheckouts();
1797            return null;
1798          }
1799    
1800          if (examinedConnections.contains(conn))
1801          {
1802            availableConnections.offer(conn);
1803            poolStatistics.incrementNumFailedCheckouts();
1804            return null;
1805          }
1806    
1807          if (conn.getConnectedAddress().equals(host) &&
1808              (port == conn.getConnectedPort()))
1809          {
1810            try
1811            {
1812              healthCheck.ensureConnectionValidForCheckout(conn);
1813              poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
1814              return conn;
1815            }
1816            catch (final LDAPException le)
1817            {
1818              debugException(le);
1819              poolStatistics.incrementNumConnectionsClosedDefunct();
1820              handleDefunctConnection(conn);
1821              continue;
1822            }
1823          }
1824    
1825          if (availableConnections.offer(conn))
1826          {
1827            examinedConnections.add(conn);
1828          }
1829        }
1830      }
1831    
1832    
1833    
1834      /**
1835       * {@inheritDoc}
1836       */
1837      @Override()
1838      public void releaseConnection(final LDAPConnection connection)
1839      {
1840        if (connection == null)
1841        {
1842          return;
1843        }
1844    
1845        connection.setConnectionPoolName(connectionPoolName);
1846        if (checkConnectionAgeOnRelease && connectionIsExpired(connection))
1847        {
1848          try
1849          {
1850            final LDAPConnection newConnection = createConnection();
1851            if (availableConnections.offer(newConnection))
1852            {
1853              connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
1854                   null, null);
1855              connection.terminate(null);
1856              poolStatistics.incrementNumConnectionsClosedExpired();
1857              lastExpiredDisconnectTime = System.currentTimeMillis();
1858            }
1859            else
1860            {
1861              newConnection.setDisconnectInfo(
1862                   DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
1863              newConnection.terminate(null);
1864              poolStatistics.incrementNumConnectionsClosedUnneeded();
1865            }
1866          }
1867          catch (final LDAPException le)
1868          {
1869            debugException(le);
1870          }
1871          return;
1872        }
1873    
1874        try
1875        {
1876          healthCheck.ensureConnectionValidForRelease(connection);
1877        }
1878        catch (LDAPException le)
1879        {
1880          releaseDefunctConnection(connection);
1881          return;
1882        }
1883    
1884        if (availableConnections.offer(connection))
1885        {
1886          poolStatistics.incrementNumReleasedValid();
1887        }
1888        else
1889        {
1890          // This means that the connection pool is full, which can happen if the
1891          // pool was empty when a request came in to retrieve a connection and
1892          // createIfNecessary was true.  In this case, we'll just close the
1893          // connection since we don't need it any more.
1894          connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1895                                       null, null);
1896          poolStatistics.incrementNumConnectionsClosedUnneeded();
1897          connection.terminate(null);
1898          return;
1899        }
1900    
1901        if (closed)
1902        {
1903          close();
1904        }
1905      }
1906    
1907    
1908    
1909      /**
1910       * Indicates that the provided connection should be removed from the pool,
1911       * and that no new connection should be created to take its place.  This may
1912       * be used to shrink the pool if such functionality is desired.
1913       *
1914       * @param  connection  The connection to be discarded.
1915       */
1916      public void discardConnection(final LDAPConnection connection)
1917      {
1918        if (connection == null)
1919        {
1920          return;
1921        }
1922    
1923        connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1924             null, null);
1925        connection.terminate(null);
1926        poolStatistics.incrementNumConnectionsClosedUnneeded();
1927    
1928        if (availableConnections.remainingCapacity() > 0)
1929        {
1930          final int newReplaceCount = failedReplaceCount.incrementAndGet();
1931          if (newReplaceCount > numConnections)
1932          {
1933            failedReplaceCount.set(numConnections);
1934          }
1935        }
1936      }
1937    
1938    
1939    
1940      /**
1941       * Performs a bind on the provided connection before releasing it back to the
1942       * pool, so that it will be authenticated as the same user as
1943       * newly-established connections.  If newly-established connections are
1944       * unauthenticated, then this method will perform an anonymous simple bind to
1945       * ensure that the resulting connection is unauthenticated.
1946       *
1947       * Releases the provided connection back to this pool.
1948       *
1949       * @param  connection  The connection to be released back to the pool after
1950       *                     being re-authenticated.
1951       */
1952      public void releaseAndReAuthenticateConnection(
1953                       final LDAPConnection connection)
1954      {
1955        if (connection == null)
1956        {
1957          return;
1958        }
1959    
1960        try
1961        {
1962          BindResult bindResult;
1963          try
1964          {
1965            if (bindRequest == null)
1966            {
1967              bindResult = connection.bind("", "");
1968            }
1969            else
1970            {
1971              bindResult = connection.bind(bindRequest.duplicate());
1972            }
1973          }
1974          catch (final LDAPBindException lbe)
1975          {
1976            debugException(lbe);
1977            bindResult = lbe.getBindResult();
1978          }
1979    
1980          try
1981          {
1982            healthCheck.ensureConnectionValidAfterAuthentication(connection,
1983                 bindResult);
1984            if (bindResult.getResultCode() != ResultCode.SUCCESS)
1985            {
1986              throw new LDAPBindException(bindResult);
1987            }
1988          }
1989          catch (final LDAPException le)
1990          {
1991            debugException(le);
1992    
1993            try
1994            {
1995              connection.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
1996              connection.terminate(null);
1997              releaseDefunctConnection(connection);
1998            }
1999            catch (final Exception e)
2000            {
2001              debugException(e);
2002            }
2003    
2004            throw le;
2005          }
2006    
2007          releaseConnection(connection);
2008        }
2009        catch (final Exception e)
2010        {
2011          debugException(e);
2012          releaseDefunctConnection(connection);
2013        }
2014      }
2015    
2016    
2017    
2018      /**
2019       * {@inheritDoc}
2020       */
2021      @Override()
2022      public void releaseDefunctConnection(final LDAPConnection connection)
2023      {
2024        if (connection == null)
2025        {
2026          return;
2027        }
2028    
2029        connection.setConnectionPoolName(connectionPoolName);
2030        poolStatistics.incrementNumConnectionsClosedDefunct();
2031        handleDefunctConnection(connection);
2032      }
2033    
2034    
2035    
2036      /**
2037       * Performs the real work of terminating a defunct connection and replacing it
2038       * with a new connection if possible.
2039       *
2040       * @param  connection  The defunct connection to be replaced.
2041       *
2042       * @return  The new connection created to take the place of the defunct
2043       *          connection, or {@code null} if no new connection was created.
2044       *          Note that if a connection is returned, it will have already been
2045       *          made available and the caller must not rely on it being unused for
2046       *          any other purpose.
2047       */
2048      private LDAPConnection handleDefunctConnection(
2049                                  final LDAPConnection connection)
2050      {
2051        connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2052                                     null);
2053        connection.terminate(null);
2054    
2055        if (closed)
2056        {
2057          return null;
2058        }
2059    
2060        if (createIfNecessary && (availableConnections.remainingCapacity() <= 0))
2061        {
2062          return null;
2063        }
2064    
2065        try
2066        {
2067          final LDAPConnection conn = createConnection();
2068          if (maxDefunctReplacementConnectionAge != null)
2069          {
2070            // Only set the maximum age if there isn't one already set for the
2071            // connection (i.e., because it was defined by the server set).
2072            if (conn.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE) == null)
2073            {
2074              conn.setAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE,
2075                   maxDefunctReplacementConnectionAge);
2076            }
2077          }
2078    
2079          if (! availableConnections.offer(conn))
2080          {
2081            conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2082                                   null, null);
2083            conn.terminate(null);
2084            return null;
2085          }
2086    
2087          return conn;
2088        }
2089        catch (LDAPException le)
2090        {
2091          debugException(le);
2092          final int newReplaceCount = failedReplaceCount.incrementAndGet();
2093          if (newReplaceCount > numConnections)
2094          {
2095            failedReplaceCount.set(numConnections);
2096          }
2097          return null;
2098        }
2099      }
2100    
2101    
2102    
2103      /**
2104       * {@inheritDoc}
2105       */
2106      @Override()
2107      public LDAPConnection replaceDefunctConnection(
2108                                 final LDAPConnection connection)
2109             throws LDAPException
2110      {
2111        poolStatistics.incrementNumConnectionsClosedDefunct();
2112        connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
2113                                     null);
2114        connection.terminate(null);
2115    
2116        if (closed)
2117        {
2118          throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get());
2119        }
2120    
2121        return createConnection();
2122      }
2123    
2124    
2125    
2126      /**
2127       * {@inheritDoc}
2128       */
2129      @Override()
2130      public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections()
2131      {
2132        return retryOperationTypes.get();
2133      }
2134    
2135    
2136    
2137      /**
2138       * {@inheritDoc}
2139       */
2140      @Override()
2141      public void setRetryFailedOperationsDueToInvalidConnections(
2142                       final Set<OperationType> operationTypes)
2143      {
2144        if ((operationTypes == null) || operationTypes.isEmpty())
2145        {
2146          retryOperationTypes.set(
2147               Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
2148        }
2149        else
2150        {
2151          final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class);
2152          s.addAll(operationTypes);
2153          retryOperationTypes.set(Collections.unmodifiableSet(s));
2154        }
2155      }
2156    
2157    
2158    
2159      /**
2160       * Indicates whether the provided connection should be considered expired.
2161       *
2162       * @param  connection  The connection for which to make the determination.
2163       *
2164       * @return  {@code true} if the provided connection should be considered
2165       *          expired, or {@code false} if not.
2166       */
2167      private boolean connectionIsExpired(final LDAPConnection connection)
2168      {
2169        // There may be a custom maximum connection age for the connection.  If that
2170        // is the case, then use that custom max age rather than the pool-default
2171        // max age.
2172        final long maxAge;
2173        final Object maxAgeObj =
2174             connection.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE);
2175        if ((maxAgeObj != null) && (maxAgeObj instanceof Long))
2176        {
2177          maxAge = (Long) maxAgeObj;
2178        }
2179        else
2180        {
2181          maxAge = maxConnectionAge;
2182        }
2183    
2184        // If connection expiration is not enabled, then there is nothing to do.
2185        if (maxAge <= 0L)
2186        {
2187          return false;
2188        }
2189    
2190        // If there is a minimum disconnect interval, then make sure that we have
2191        // not closed another expired connection too recently.
2192        final long currentTime = System.currentTimeMillis();
2193        if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval)
2194        {
2195          return false;
2196        }
2197    
2198        // Get the age of the connection and see if it is expired.
2199        final long connectionAge = currentTime - connection.getConnectTime();
2200        return (connectionAge > maxAge);
2201      }
2202    
2203    
2204    
2205      /**
2206       * {@inheritDoc}
2207       */
2208      @Override()
2209      public String getConnectionPoolName()
2210      {
2211        return connectionPoolName;
2212      }
2213    
2214    
2215    
2216      /**
2217       * {@inheritDoc}
2218       */
2219      @Override()
2220      public void setConnectionPoolName(final String connectionPoolName)
2221      {
2222        this.connectionPoolName = connectionPoolName;
2223        for (final LDAPConnection c : availableConnections)
2224        {
2225          c.setConnectionPoolName(connectionPoolName);
2226        }
2227      }
2228    
2229    
2230    
2231      /**
2232       * Indicates whether the connection pool should create a new connection if one
2233       * is requested when there are none available.
2234       *
2235       * @return  {@code true} if a new connection should be created if none are
2236       *          available when a request is received, or {@code false} if an
2237       *          exception should be thrown to indicate that no connection is
2238       *          available.
2239       */
2240      public boolean getCreateIfNecessary()
2241      {
2242        return createIfNecessary;
2243      }
2244    
2245    
2246    
2247      /**
2248       * Specifies whether the connection pool should create a new connection if one
2249       * is requested when there are none available.
2250       *
2251       * @param  createIfNecessary  Specifies whether the connection pool should
2252       *                            create a new connection if one is requested when
2253       *                            there are none available.
2254       */
2255      public void setCreateIfNecessary(final boolean createIfNecessary)
2256      {
2257        this.createIfNecessary = createIfNecessary;
2258      }
2259    
2260    
2261    
2262      /**
2263       * Retrieves the maximum length of time in milliseconds to wait for a
2264       * connection to become available when trying to obtain a connection from the
2265       * pool.
2266       *
2267       * @return  The maximum length of time in milliseconds to wait for a
2268       *          connection to become available when trying to obtain a connection
2269       *          from the pool, or zero to indicate that the pool should not block
2270       *          at all if no connections are available and that it should either
2271       *          create a new connection or throw an exception.
2272       */
2273      public long getMaxWaitTimeMillis()
2274      {
2275        return maxWaitTime;
2276      }
2277    
2278    
2279    
2280      /**
2281       * Specifies the maximum length of time in milliseconds to wait for a
2282       * connection to become available when trying to obtain a connection from the
2283       * pool.
2284       *
2285       * @param  maxWaitTime  The maximum length of time in milliseconds to wait for
2286       *                      a connection to become available when trying to obtain
2287       *                      a connection from the pool.  A value of zero should be
2288       *                      used to indicate that the pool should not block at all
2289       *                      if no connections are available and that it should
2290       *                      either create a new connection or throw an exception.
2291       */
2292      public void setMaxWaitTimeMillis(final long maxWaitTime)
2293      {
2294        if (maxWaitTime > 0L)
2295        {
2296          this.maxWaitTime = maxWaitTime;
2297        }
2298        else
2299        {
2300          this.maxWaitTime = 0L;
2301        }
2302      }
2303    
2304    
2305    
2306      /**
2307       * Retrieves the maximum length of time in milliseconds that a connection in
2308       * this pool may be established before it is closed and replaced with another
2309       * connection.
2310       *
2311       * @return  The maximum length of time in milliseconds that a connection in
2312       *          this pool may be established before it is closed and replaced with
2313       *          another connection, or {@code 0L} if no maximum age should be
2314       *          enforced.
2315       */
2316      public long getMaxConnectionAgeMillis()
2317      {
2318        return maxConnectionAge;
2319      }
2320    
2321    
2322    
2323      /**
2324       * Specifies the maximum length of time in milliseconds that a connection in
2325       * this pool may be established before it should be closed and replaced with
2326       * another connection.
2327       *
2328       * @param  maxConnectionAge  The maximum length of time in milliseconds that a
2329       *                           connection in this pool may be established before
2330       *                           it should be closed and replaced with another
2331       *                           connection.  A value of zero indicates that no
2332       *                           maximum age should be enforced.
2333       */
2334      public void setMaxConnectionAgeMillis(final long maxConnectionAge)
2335      {
2336        if (maxConnectionAge > 0L)
2337        {
2338          this.maxConnectionAge = maxConnectionAge;
2339        }
2340        else
2341        {
2342          this.maxConnectionAge = 0L;
2343        }
2344      }
2345    
2346    
2347    
2348      /**
2349       * Retrieves the maximum connection age that should be used for connections
2350       * that were created in order to replace defunct connections.  It is possible
2351       * to define a custom maximum connection age for these connections to allow
2352       * them to be closed and re-established more quickly to allow for a
2353       * potentially quicker fail-back to a normal state.  Note, that if this
2354       * capability is to be used, then the maximum age for these connections should
2355       * be long enough to allow the problematic server to become available again
2356       * under normal circumstances (e.g., it should be long enough for at least a
2357       * shutdown and restart of the server, plus some overhead for potentially
2358       * performing routine maintenance while the server is offline, or a chance for
2359       * an administrator to be made available that a server has gone down).
2360       *
2361       * @return  The maximum connection age that should be used for connections
2362       *          that were created in order to replace defunct connections, a value
2363       *          of zero to indicate that no maximum age should be enforced, or
2364       *          {@code null} if the value returned by the
2365       *          {@code getMaxConnectionAgeMillis()} method should be used.
2366       */
2367      public Long getMaxDefunctReplacementConnectionAgeMillis()
2368      {
2369        return maxDefunctReplacementConnectionAge;
2370      }
2371    
2372    
2373    
2374      /**
2375       * Specifies the maximum connection age that should be used for connections
2376       * that were created in order to replace defunct connections.  It is possible
2377       * to define a custom maximum connection age for these connections to allow
2378       * them to be closed and re-established more quickly to allow for a
2379       * potentially quicker fail-back to a normal state.  Note, that if this
2380       * capability is to be used, then the maximum age for these connections should
2381       * be long enough to allow the problematic server to become available again
2382       * under normal circumstances (e.g., it should be long enough for at least a
2383       * shutdown and restart of the server, plus some overhead for potentially
2384       * performing routine maintenance while the server is offline, or a chance for
2385       * an administrator to be made available that a server has gone down).
2386       *
2387       * @param  maxDefunctReplacementConnectionAge  The maximum connection age that
2388       *              should be used for connections that were created in order to
2389       *              replace defunct connections.  It may be zero if no maximum age
2390       *              should be enforced for such connections, or it may be
2391       *              {@code null} if the value returned by the
2392       *              {@code getMaxConnectionAgeMillis()} method should be used.
2393       */
2394      public void setMaxDefunctReplacementConnectionAgeMillis(
2395                       final Long maxDefunctReplacementConnectionAge)
2396      {
2397        if (maxDefunctReplacementConnectionAge == null)
2398        {
2399          this.maxDefunctReplacementConnectionAge = null;
2400        }
2401        else if (maxDefunctReplacementConnectionAge > 0L)
2402        {
2403          this.maxDefunctReplacementConnectionAge =
2404               maxDefunctReplacementConnectionAge;
2405        }
2406        else
2407        {
2408          this.maxDefunctReplacementConnectionAge = 0L;
2409        }
2410      }
2411    
2412    
2413    
2414      /**
2415       * Indicates whether to check the age of a connection against the configured
2416       * maximum connection age whenever it is released to the pool.  By default,
2417       * connection age is evaluated in the background using the health check
2418       * thread, but it is also possible to configure the pool to additionally
2419       * examine the age of a connection when it is returned to the pool.
2420       * <BR><BR>
2421       * Performing connection age evaluation only in the background will ensure
2422       * that connections are only closed and re-established in a single-threaded
2423       * manner, which helps minimize the load against the target server, but only
2424       * checks connections that are not in use when the health check thread is
2425       * active.  If the pool is configured to also evaluate the connection age when
2426       * connections are returned to the pool, then it may help ensure that the
2427       * maximum connection age is honored more strictly for all connections, but
2428       * in busy applications may lead to cases in which multiple connections are
2429       * closed and re-established simultaneously, which may increase load against
2430       * the directory server.  The {@code setMinDisconnectIntervalMillis(long)}
2431       * method may be used to help mitigate the potential performance impact of
2432       * closing and re-establishing multiple connections simultaneously.
2433       *
2434       * @return  {@code true} if the connection pool should check connection age in
2435       *          both the background health check thread and when connections are
2436       *          released to the pool, or {@code false} if the connection age
2437       *          should only be checked by the background health check thread.
2438       */
2439      public boolean checkConnectionAgeOnRelease()
2440      {
2441        return checkConnectionAgeOnRelease;
2442      }
2443    
2444    
2445    
2446      /**
2447       * Specifies whether to check the age of a connection against the configured
2448       * maximum connection age whenever it is released to the pool.  By default,
2449       * connection age is evaluated in the background using the health check
2450       * thread, but it is also possible to configure the pool to additionally
2451       * examine the age of a connection when it is returned to the pool.
2452       * <BR><BR>
2453       * Performing connection age evaluation only in the background will ensure
2454       * that connections are only closed and re-established in a single-threaded
2455       * manner, which helps minimize the load against the target server, but only
2456       * checks connections that are not in use when the health check thread is
2457       * active.  If the pool is configured to also evaluate the connection age when
2458       * connections are returned to the pool, then it may help ensure that the
2459       * maximum connection age is honored more strictly for all connections, but
2460       * in busy applications may lead to cases in which multiple connections are
2461       * closed and re-established simultaneously, which may increase load against
2462       * the directory server.  The {@code setMinDisconnectIntervalMillis(long)}
2463       * method may be used to help mitigate the potential performance impact of
2464       * closing and re-establishing multiple connections simultaneously.
2465       *
2466       * @param  checkConnectionAgeOnRelease  If {@code true}, this indicates that
2467       *                                      the connection pool should check
2468       *                                      connection age in both the background
2469       *                                      health check thread and when
2470       *                                      connections are released to the pool.
2471       *                                      If {@code false}, this indicates that
2472       *                                      the connection pool should check
2473       *                                      connection age only in the background
2474       *                                      health check thread.
2475       */
2476      public void setCheckConnectionAgeOnRelease(
2477                       final boolean checkConnectionAgeOnRelease)
2478      {
2479        this.checkConnectionAgeOnRelease = checkConnectionAgeOnRelease;
2480      }
2481    
2482    
2483    
2484      /**
2485       * Retrieves the minimum length of time in milliseconds that should pass
2486       * between connections closed because they have been established for longer
2487       * than the maximum connection age.
2488       *
2489       * @return  The minimum length of time in milliseconds that should pass
2490       *          between connections closed because they have been established for
2491       *          longer than the maximum connection age, or {@code 0L} if expired
2492       *          connections may be closed as quickly as they are identified.
2493       */
2494      public long getMinDisconnectIntervalMillis()
2495      {
2496        return minDisconnectInterval;
2497      }
2498    
2499    
2500    
2501      /**
2502       * Specifies the minimum length of time in milliseconds that should pass
2503       * between connections closed because they have been established for longer
2504       * than the maximum connection age.
2505       *
2506       * @param  minDisconnectInterval  The minimum length of time in milliseconds
2507       *                                that should pass between connections closed
2508       *                                because they have been established for
2509       *                                longer than the maximum connection age.  A
2510       *                                value less than or equal to zero indicates
2511       *                                that no minimum time should be enforced.
2512       */
2513      public void setMinDisconnectIntervalMillis(final long minDisconnectInterval)
2514      {
2515        if (minDisconnectInterval > 0)
2516        {
2517          this.minDisconnectInterval = minDisconnectInterval;
2518        }
2519        else
2520        {
2521          this.minDisconnectInterval = 0L;
2522        }
2523      }
2524    
2525    
2526    
2527      /**
2528       * {@inheritDoc}
2529       */
2530      @Override()
2531      public LDAPConnectionPoolHealthCheck getHealthCheck()
2532      {
2533        return healthCheck;
2534      }
2535    
2536    
2537    
2538      /**
2539       * Sets the health check implementation for this connection pool.
2540       *
2541       * @param  healthCheck  The health check implementation for this connection
2542       *                      pool.  It must not be {@code null}.
2543       */
2544      public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck)
2545      {
2546        ensureNotNull(healthCheck);
2547        this.healthCheck = healthCheck;
2548      }
2549    
2550    
2551    
2552      /**
2553       * {@inheritDoc}
2554       */
2555      @Override()
2556      public long getHealthCheckIntervalMillis()
2557      {
2558        return healthCheckInterval;
2559      }
2560    
2561    
2562    
2563      /**
2564       * {@inheritDoc}
2565       */
2566      @Override()
2567      public void setHealthCheckIntervalMillis(final long healthCheckInterval)
2568      {
2569        ensureTrue(healthCheckInterval > 0L,
2570             "LDAPConnectionPool.healthCheckInterval must be greater than 0.");
2571        this.healthCheckInterval = healthCheckInterval;
2572        healthCheckThread.wakeUp();
2573      }
2574    
2575    
2576    
2577      /**
2578       * Indicates whether health check processing for connections operating in
2579       * synchronous mode should include attempting to perform a read from each
2580       * connection with a very short timeout.  This can help detect unsolicited
2581       * responses and unexpected connection closures in a more timely manner.  This
2582       * will be ignored for connections not operating in synchronous mode.
2583       *
2584       * @return  {@code true} if health check processing for connections operating
2585       *          in synchronous mode should include a read attempt with a very
2586       *          short timeout, or {@code false} if not.
2587       */
2588      public boolean trySynchronousReadDuringHealthCheck()
2589      {
2590        return trySynchronousReadDuringHealthCheck;
2591      }
2592    
2593    
2594    
2595      /**
2596       * Specifies whether health check processing for connections operating in
2597       * synchronous mode should include attempting to perform a read from each
2598       * connection with a very short timeout.
2599       *
2600       * @param  trySynchronousReadDuringHealthCheck  Indicates whether health check
2601       *                                              processing for connections
2602       *                                              operating in synchronous mode
2603       *                                              should include attempting to
2604       *                                              perform a read from each
2605       *                                              connection with a very short
2606       *                                              timeout.
2607       */
2608      public void setTrySynchronousReadDuringHealthCheck(
2609                       final boolean trySynchronousReadDuringHealthCheck)
2610      {
2611        this.trySynchronousReadDuringHealthCheck =
2612             trySynchronousReadDuringHealthCheck;
2613      }
2614    
2615    
2616    
2617      /**
2618       * {@inheritDoc}
2619       */
2620      @Override()
2621      protected void doHealthCheck()
2622      {
2623        invokeHealthCheck(null, true);
2624      }
2625    
2626    
2627    
2628      /**
2629       * Invokes a synchronous one-time health-check against the connections in this
2630       * pool that are not currently in use.  This will be independent of any
2631       * background health checking that may be automatically performed by the pool.
2632       *
2633       * @param  healthCheck         The health check to use.  If this is
2634       *                             {@code null}, then the pool's
2635       *                             currently-configured health check (if any) will
2636       *                             be used.  If this is {@code null} and there is
2637       *                             no health check configured for the pool, then
2638       *                             only a basic set of checks.
2639       * @param  checkForExpiration  Indicates whether to check to see if any
2640       *                             connections have been established for longer
2641       *                             than the maximum connection age.  If this is
2642       *                             {@code true} then any expired connections will
2643       *                             be closed and replaced with newly-established
2644       *                             connections.
2645       *
2646       * @return  An object with information about the result of the health check
2647       *          processing.
2648       */
2649      public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2650                  final LDAPConnectionPoolHealthCheck healthCheck,
2651                  final boolean checkForExpiration)
2652      {
2653        return invokeHealthCheck(healthCheck, checkForExpiration,
2654             checkForExpiration);
2655      }
2656    
2657    
2658    
2659      /**
2660       * Invokes a synchronous one-time health-check against the connections in this
2661       * pool that are not currently in use.  This will be independent of any
2662       * background health checking that may be automatically performed by the pool.
2663       *
2664       * @param  healthCheck             The health check to use.  If this is
2665       *                                 {@code null}, then the pool's
2666       *                                 currently-configured health check (if any)
2667       *                                 will be used.  If this is {@code null} and
2668       *                                 there is no health check configured for the
2669       *                                 pool, then only a basic set of checks.
2670       * @param  checkForExpiration      Indicates whether to check to see if any
2671       *                                 connections have been established for
2672       *                                 longer than the maximum connection age.  If
2673       *                                 this is {@code true} then any expired
2674       *                                 connections will be closed and replaced
2675       *                                 with newly-established connections.
2676       * @param  checkMinConnectionGoal  Indicates whether to check to see if the
2677       *                                 currently-available number of connections
2678       *                                 is less than the minimum available
2679       *                                 connection goal.  If this is {@code true}
2680       *                                 the minimum available connection goal is
2681       *                                 greater than zero, and the number of
2682       *                                 currently-available connections is less
2683       *                                 than the goal, then this method will
2684       *                                 attempt to create enough new connections to
2685       *                                 reach the goal.
2686       *
2687       * @return  An object with information about the result of the health check
2688       *          processing.
2689       */
2690      public LDAPConnectionPoolHealthCheckResult invokeHealthCheck(
2691                  final LDAPConnectionPoolHealthCheck healthCheck,
2692                  final boolean checkForExpiration,
2693                  final boolean checkMinConnectionGoal)
2694      {
2695        // Determine which health check to use.
2696        final LDAPConnectionPoolHealthCheck hc;
2697        if (healthCheck == null)
2698        {
2699          hc = this.healthCheck;
2700        }
2701        else
2702        {
2703          hc = healthCheck;
2704        }
2705    
2706    
2707        // Create a set used to hold connections that we've already examined.  If we
2708        // encounter the same connection twice, then we know that we don't need to
2709        // do any more work.
2710        final HashSet<LDAPConnection> examinedConnections =
2711             new HashSet<LDAPConnection>(numConnections);
2712        int numExamined = 0;
2713        int numDefunct = 0;
2714        int numExpired = 0;
2715    
2716        for (int i=0; i < numConnections; i++)
2717        {
2718          LDAPConnection conn = availableConnections.poll();
2719          if (conn == null)
2720          {
2721            break;
2722          }
2723          else if (examinedConnections.contains(conn))
2724          {
2725            if (! availableConnections.offer(conn))
2726            {
2727              conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2728                                     null, null);
2729              poolStatistics.incrementNumConnectionsClosedUnneeded();
2730              conn.terminate(null);
2731            }
2732            break;
2733          }
2734    
2735          numExamined++;
2736          if (! conn.isConnected())
2737          {
2738            numDefunct++;
2739            poolStatistics.incrementNumConnectionsClosedDefunct();
2740            conn = handleDefunctConnection(conn);
2741            if (conn != null)
2742            {
2743              examinedConnections.add(conn);
2744            }
2745          }
2746          else
2747          {
2748            if (checkForExpiration && connectionIsExpired(conn))
2749            {
2750              numExpired++;
2751    
2752              try
2753              {
2754                final LDAPConnection newConnection = createConnection();
2755                if (availableConnections.offer(newConnection))
2756                {
2757                  examinedConnections.add(newConnection);
2758                  conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
2759                       null, null);
2760                  conn.terminate(null);
2761                  poolStatistics.incrementNumConnectionsClosedExpired();
2762                  lastExpiredDisconnectTime = System.currentTimeMillis();
2763                  continue;
2764                }
2765                else
2766                {
2767                  newConnection.setDisconnectInfo(
2768                       DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
2769                  newConnection.terminate(null);
2770                  poolStatistics.incrementNumConnectionsClosedUnneeded();
2771                }
2772              }
2773              catch (final LDAPException le)
2774              {
2775                debugException(le);
2776              }
2777            }
2778    
2779    
2780            // If the connection is operating in synchronous mode, then try to read
2781            // a message on it using an extremely short timeout.  This can help
2782            // detect a connection closure or unsolicited notification in a more
2783            // timely manner than if we had to wait for the client code to try to
2784            // use the connection.
2785            if (trySynchronousReadDuringHealthCheck && conn.synchronousMode())
2786            {
2787              int previousTimeout = Integer.MIN_VALUE;
2788              Socket s = null;
2789              try
2790              {
2791                s = conn.getConnectionInternals(true).getSocket();
2792                previousTimeout = s.getSoTimeout();
2793                s.setSoTimeout(1);
2794    
2795                final LDAPResponse response = conn.readResponse(0);
2796                if (response instanceof ConnectionClosedResponse)
2797                {
2798                  numDefunct++;
2799                  conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2800                       ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
2801                  poolStatistics.incrementNumConnectionsClosedDefunct();
2802                  conn = handleDefunctConnection(conn);
2803                  if (conn != null)
2804                  {
2805                    examinedConnections.add(conn);
2806                  }
2807                  continue;
2808                }
2809                else if (response instanceof ExtendedResult)
2810                {
2811                  // This means we got an unsolicited response.  It could be a
2812                  // notice of disconnection, or it could be something else, but in
2813                  // any case we'll send it to the connection's unsolicited
2814                  // notification handler (if one is defined).
2815                  final UnsolicitedNotificationHandler h = conn.
2816                       getConnectionOptions().getUnsolicitedNotificationHandler();
2817                  if (h != null)
2818                  {
2819                    h.handleUnsolicitedNotification(conn,
2820                         (ExtendedResult) response);
2821                  }
2822                }
2823                else if (response instanceof LDAPResult)
2824                {
2825                  final LDAPResult r = (LDAPResult) response;
2826                  if (r.getResultCode() == ResultCode.SERVER_DOWN)
2827                  {
2828                    numDefunct++;
2829                    conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2830                         ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
2831                    poolStatistics.incrementNumConnectionsClosedDefunct();
2832                    conn = handleDefunctConnection(conn);
2833                    if (conn != null)
2834                    {
2835                      examinedConnections.add(conn);
2836                    }
2837                    continue;
2838                  }
2839                }
2840              }
2841              catch (final LDAPException le)
2842              {
2843                if (le.getResultCode() == ResultCode.TIMEOUT)
2844                {
2845                  debugException(Level.FINEST, le);
2846                }
2847                else
2848                {
2849                  debugException(le);
2850                  numDefunct++;
2851                  conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2852                       ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(
2853                            getExceptionMessage(le)), le);
2854                  poolStatistics.incrementNumConnectionsClosedDefunct();
2855                  conn = handleDefunctConnection(conn);
2856                  if (conn != null)
2857                  {
2858                    examinedConnections.add(conn);
2859                  }
2860                  continue;
2861                }
2862              }
2863              catch (final Exception e)
2864              {
2865                debugException(e);
2866                numDefunct++;
2867                conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2868                     ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(getExceptionMessage(e)),
2869                     e);
2870                poolStatistics.incrementNumConnectionsClosedDefunct();
2871                conn = handleDefunctConnection(conn);
2872                if (conn != null)
2873                {
2874                  examinedConnections.add(conn);
2875                }
2876                continue;
2877              }
2878              finally
2879              {
2880                if (previousTimeout != Integer.MIN_VALUE)
2881                {
2882                  try
2883                  {
2884                    s.setSoTimeout(previousTimeout);
2885                  }
2886                  catch (final Exception e)
2887                  {
2888                    debugException(e);
2889                    numDefunct++;
2890                    conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
2891                         null, e);
2892                    poolStatistics.incrementNumConnectionsClosedDefunct();
2893                    conn = handleDefunctConnection(conn);
2894                    if (conn != null)
2895                    {
2896                      examinedConnections.add(conn);
2897                    }
2898                    continue;
2899                  }
2900                }
2901              }
2902            }
2903    
2904            try
2905            {
2906              hc.ensureConnectionValidForContinuedUse(conn);
2907              if (availableConnections.offer(conn))
2908              {
2909                examinedConnections.add(conn);
2910              }
2911              else
2912              {
2913                conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2914                                       null, null);
2915                poolStatistics.incrementNumConnectionsClosedUnneeded();
2916                conn.terminate(null);
2917              }
2918            }
2919            catch (Exception e)
2920            {
2921              debugException(e);
2922              numDefunct++;
2923              poolStatistics.incrementNumConnectionsClosedDefunct();
2924              conn = handleDefunctConnection(conn);
2925              if (conn != null)
2926              {
2927                examinedConnections.add(conn);
2928              }
2929            }
2930          }
2931        }
2932    
2933        if (checkMinConnectionGoal)
2934        {
2935          try
2936          {
2937            final int neededConnections =
2938                 minConnectionGoal - availableConnections.size();
2939            for (int i=0; i < neededConnections; i++)
2940            {
2941              final LDAPConnection conn = createConnection(hc);
2942              if (! availableConnections.offer(conn))
2943              {
2944                conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
2945                                       null, null);
2946                poolStatistics.incrementNumConnectionsClosedUnneeded();
2947                conn.terminate(null);
2948                break;
2949              }
2950            }
2951          }
2952          catch (final Exception e)
2953          {
2954            debugException(e);
2955          }
2956        }
2957    
2958        return new LDAPConnectionPoolHealthCheckResult(numExamined, numExpired,
2959             numDefunct);
2960      }
2961    
2962    
2963    
2964      /**
2965       * {@inheritDoc}
2966       */
2967      @Override()
2968      public int getCurrentAvailableConnections()
2969      {
2970        return availableConnections.size();
2971      }
2972    
2973    
2974    
2975      /**
2976       * {@inheritDoc}
2977       */
2978      @Override()
2979      public int getMaximumAvailableConnections()
2980      {
2981        return numConnections;
2982      }
2983    
2984    
2985    
2986      /**
2987       * Retrieves the goal for the minimum number of available connections that the
2988       * pool should try to maintain for immediate use.  If this goal is greater
2989       * than zero, then the health checking process will attempt to create enough
2990       * new connections to achieve this goal.
2991       *
2992       * @return  The goal for the minimum number of available connections that the
2993       *          pool should try to maintain for immediate use, or zero if it will
2994       *          not try to maintain a minimum number of available connections.
2995       */
2996      public int getMinimumAvailableConnectionGoal()
2997      {
2998        return minConnectionGoal;
2999      }
3000    
3001    
3002    
3003      /**
3004       * Specifies the goal for the minimum number of available connections that the
3005       * pool should try to maintain for immediate use.  If this goal is greater
3006       * than zero, then the health checking process will attempt to create enough
3007       * new connections to achieve this goal.
3008       *
3009       * @param  goal  The goal for the minimum number of available connections that
3010       *               the pool should try to maintain for immediate use.  A value
3011       *               less than or equal to zero indicates that the pool should not
3012       *               try to maintain a minimum number of available connections.
3013       */
3014      public void setMinimumAvailableConnectionGoal(final int goal)
3015      {
3016        if (goal > numConnections)
3017        {
3018          minConnectionGoal = numConnections;
3019        }
3020        else if (goal > 0)
3021        {
3022          minConnectionGoal = goal;
3023        }
3024        else
3025        {
3026          minConnectionGoal = 0;
3027        }
3028      }
3029    
3030    
3031    
3032      /**
3033       * {@inheritDoc}
3034       */
3035      @Override()
3036      public LDAPConnectionPoolStatistics getConnectionPoolStatistics()
3037      {
3038        return poolStatistics;
3039      }
3040    
3041    
3042    
3043      /**
3044       * Attempts to reduce the number of connections available for use in the pool.
3045       * Note that this will be a best-effort attempt to reach the desired number
3046       * of connections, as other threads interacting with the connection pool may
3047       * check out and/or release connections that cause the number of available
3048       * connections to fluctuate.
3049       *
3050       * @param  connectionsToRetain  The number of connections that should be
3051       *                              retained for use in the connection pool.
3052       */
3053      public void shrinkPool(final int connectionsToRetain)
3054      {
3055        while (availableConnections.size() > connectionsToRetain)
3056        {
3057          final LDAPConnection conn;
3058          try
3059          {
3060            conn = getConnection();
3061          }
3062          catch (final LDAPException le)
3063          {
3064            return;
3065          }
3066    
3067          if (availableConnections.size() >= connectionsToRetain)
3068          {
3069            discardConnection(conn);
3070          }
3071          else
3072          {
3073            releaseConnection(conn);
3074            return;
3075          }
3076        }
3077      }
3078    
3079    
3080    
3081      /**
3082       * Closes this connection pool in the event that it becomes unreferenced.
3083       *
3084       * @throws  Throwable  If an unexpected problem occurs.
3085       */
3086      @Override()
3087      protected void finalize()
3088                throws Throwable
3089      {
3090        super.finalize();
3091    
3092        close();
3093      }
3094    
3095    
3096    
3097      /**
3098       * {@inheritDoc}
3099       */
3100      @Override()
3101      public void toString(final StringBuilder buffer)
3102      {
3103        buffer.append("LDAPConnectionPool(");
3104    
3105        final String name = connectionPoolName;
3106        if (name != null)
3107        {
3108          buffer.append("name='");
3109          buffer.append(name);
3110          buffer.append("', ");
3111        }
3112    
3113        buffer.append("serverSet=");
3114        serverSet.toString(buffer);
3115        buffer.append(", maxConnections=");
3116        buffer.append(numConnections);
3117        buffer.append(')');
3118      }
3119    }