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