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