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