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