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