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