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