001/* 002 * Copyright 2007-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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 StaticUtils.rethrowIfError(t); 1673 throw new LDAPException(ResultCode.LOCAL_ERROR, 1674 ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t); 1675 } 1676 } 1677 1678 1679 // If we've gotten here, then the bind operation should be re-tried on a 1680 // newly-established connection. 1681 conn = replaceDefunctConnection(conn); 1682 1683 try 1684 { 1685 final BindResult result = conn.bind(bindRequest); 1686 releaseAndReAuthenticateConnection(conn); 1687 return result; 1688 } 1689 catch (final Throwable t) 1690 { 1691 Debug.debugException(t); 1692 1693 if (t instanceof LDAPException) 1694 { 1695 final LDAPException le = (LDAPException) t; 1696 1697 try 1698 { 1699 healthCheck.ensureConnectionValidAfterException(conn, le); 1700 releaseAndReAuthenticateConnection(conn); 1701 } 1702 catch (final Exception e) 1703 { 1704 Debug.debugException(e); 1705 releaseDefunctConnection(conn); 1706 } 1707 1708 throw le; 1709 } 1710 else 1711 { 1712 releaseDefunctConnection(conn); 1713 StaticUtils.rethrowIfError(t); 1714 throw new LDAPException(ResultCode.LOCAL_ERROR, 1715 ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t); 1716 } 1717 } 1718 } 1719 1720 1721 1722 /** 1723 * {@inheritDoc} 1724 */ 1725 @Override() 1726 public LDAPConnection getConnection() 1727 throws LDAPException 1728 { 1729 if (closed) 1730 { 1731 poolStatistics.incrementNumFailedCheckouts(); 1732 throw new LDAPException(ResultCode.CONNECT_ERROR, 1733 ERR_POOL_CLOSED.get()); 1734 } 1735 1736 LDAPConnection conn = availableConnections.poll(); 1737 if (conn != null) 1738 { 1739 if (conn.isConnected()) 1740 { 1741 try 1742 { 1743 healthCheck.ensureConnectionValidForCheckout(conn); 1744 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1745 return conn; 1746 } 1747 catch (final LDAPException le) 1748 { 1749 Debug.debugException(le); 1750 } 1751 } 1752 1753 poolStatistics.incrementNumConnectionsClosedDefunct(); 1754 handleDefunctConnection(conn); 1755 for (int i=0; i < numConnections; i++) 1756 { 1757 conn = availableConnections.poll(); 1758 if (conn == null) 1759 { 1760 break; 1761 } 1762 else if (conn.isConnected()) 1763 { 1764 try 1765 { 1766 healthCheck.ensureConnectionValidForCheckout(conn); 1767 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1768 return conn; 1769 } 1770 catch (final LDAPException le) 1771 { 1772 Debug.debugException(le); 1773 poolStatistics.incrementNumConnectionsClosedDefunct(); 1774 handleDefunctConnection(conn); 1775 } 1776 } 1777 else 1778 { 1779 poolStatistics.incrementNumConnectionsClosedDefunct(); 1780 handleDefunctConnection(conn); 1781 } 1782 } 1783 } 1784 1785 if (failedReplaceCount.get() > 0) 1786 { 1787 final int newReplaceCount = failedReplaceCount.getAndDecrement(); 1788 if (newReplaceCount > 0) 1789 { 1790 try 1791 { 1792 conn = createConnection(); 1793 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection(); 1794 return conn; 1795 } 1796 catch (final LDAPException le) 1797 { 1798 Debug.debugException(le); 1799 failedReplaceCount.incrementAndGet(); 1800 poolStatistics.incrementNumFailedCheckouts(); 1801 throw le; 1802 } 1803 } 1804 else 1805 { 1806 failedReplaceCount.incrementAndGet(); 1807 poolStatistics.incrementNumFailedCheckouts(); 1808 throw new LDAPException(ResultCode.CONNECT_ERROR, 1809 ERR_POOL_NO_CONNECTIONS.get()); 1810 } 1811 } 1812 1813 if (maxWaitTime > 0) 1814 { 1815 try 1816 { 1817 conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS); 1818 if (conn != null) 1819 { 1820 try 1821 { 1822 healthCheck.ensureConnectionValidForCheckout(conn); 1823 poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting(); 1824 return conn; 1825 } 1826 catch (final LDAPException le) 1827 { 1828 Debug.debugException(le); 1829 poolStatistics.incrementNumConnectionsClosedDefunct(); 1830 handleDefunctConnection(conn); 1831 } 1832 } 1833 } 1834 catch (final InterruptedException ie) 1835 { 1836 Debug.debugException(ie); 1837 Thread.currentThread().interrupt(); 1838 throw new LDAPException(ResultCode.LOCAL_ERROR, 1839 ERR_POOL_CHECKOUT_INTERRUPTED.get(), ie); 1840 } 1841 } 1842 1843 if (createIfNecessary) 1844 { 1845 try 1846 { 1847 conn = createConnection(); 1848 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection(); 1849 return conn; 1850 } 1851 catch (final LDAPException le) 1852 { 1853 Debug.debugException(le); 1854 poolStatistics.incrementNumFailedCheckouts(); 1855 throw le; 1856 } 1857 } 1858 else 1859 { 1860 poolStatistics.incrementNumFailedCheckouts(); 1861 throw new LDAPException(ResultCode.CONNECT_ERROR, 1862 ERR_POOL_NO_CONNECTIONS.get()); 1863 } 1864 } 1865 1866 1867 1868 /** 1869 * Attempts to retrieve a connection from the pool that is established to the 1870 * specified server. Note that this method will only attempt to return an 1871 * existing connection that is currently available, and will not create a 1872 * connection or wait for any checked-out connections to be returned. 1873 * 1874 * @param host The address of the server to which the desired connection 1875 * should be established. This must not be {@code null}, and 1876 * this must exactly match the address provided for the initial 1877 * connection or the {@code ServerSet} used to create the pool. 1878 * @param port The port of the server to which the desired connection should 1879 * be established. 1880 * 1881 * @return A connection that is established to the specified server, or 1882 * {@code null} if there are no available connections established to 1883 * the specified server. 1884 */ 1885 public LDAPConnection getConnection(final String host, final int port) 1886 { 1887 if (closed) 1888 { 1889 poolStatistics.incrementNumFailedCheckouts(); 1890 return null; 1891 } 1892 1893 final HashSet<LDAPConnection> examinedConnections = 1894 new HashSet<>(StaticUtils.computeMapCapacity(numConnections)); 1895 while (true) 1896 { 1897 final LDAPConnection conn = availableConnections.poll(); 1898 if (conn == null) 1899 { 1900 poolStatistics.incrementNumFailedCheckouts(); 1901 return null; 1902 } 1903 1904 if (examinedConnections.contains(conn)) 1905 { 1906 availableConnections.offer(conn); 1907 poolStatistics.incrementNumFailedCheckouts(); 1908 return null; 1909 } 1910 1911 if (conn.getConnectedAddress().equals(host) && 1912 (port == conn.getConnectedPort())) 1913 { 1914 try 1915 { 1916 healthCheck.ensureConnectionValidForCheckout(conn); 1917 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1918 return conn; 1919 } 1920 catch (final LDAPException le) 1921 { 1922 Debug.debugException(le); 1923 poolStatistics.incrementNumConnectionsClosedDefunct(); 1924 handleDefunctConnection(conn); 1925 continue; 1926 } 1927 } 1928 1929 if (availableConnections.offer(conn)) 1930 { 1931 examinedConnections.add(conn); 1932 } 1933 } 1934 } 1935 1936 1937 1938 /** 1939 * {@inheritDoc} 1940 */ 1941 @Override() 1942 public void releaseConnection(final LDAPConnection connection) 1943 { 1944 if (connection == null) 1945 { 1946 return; 1947 } 1948 1949 connection.setConnectionPoolName(connectionPoolName); 1950 if (checkConnectionAgeOnRelease && connectionIsExpired(connection)) 1951 { 1952 try 1953 { 1954 final LDAPConnection newConnection = createConnection(); 1955 if (availableConnections.offer(newConnection)) 1956 { 1957 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED, 1958 null, null); 1959 connection.terminate(null); 1960 poolStatistics.incrementNumConnectionsClosedExpired(); 1961 lastExpiredDisconnectTime = System.currentTimeMillis(); 1962 } 1963 else 1964 { 1965 newConnection.setDisconnectInfo( 1966 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null); 1967 newConnection.terminate(null); 1968 poolStatistics.incrementNumConnectionsClosedUnneeded(); 1969 } 1970 } 1971 catch (final LDAPException le) 1972 { 1973 Debug.debugException(le); 1974 } 1975 return; 1976 } 1977 1978 try 1979 { 1980 healthCheck.ensureConnectionValidForRelease(connection); 1981 } 1982 catch (final LDAPException le) 1983 { 1984 releaseDefunctConnection(connection); 1985 return; 1986 } 1987 1988 if (availableConnections.offer(connection)) 1989 { 1990 poolStatistics.incrementNumReleasedValid(); 1991 } 1992 else 1993 { 1994 // This means that the connection pool is full, which can happen if the 1995 // pool was empty when a request came in to retrieve a connection and 1996 // createIfNecessary was true. In this case, we'll just close the 1997 // connection since we don't need it any more. 1998 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 1999 null, null); 2000 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2001 connection.terminate(null); 2002 return; 2003 } 2004 2005 if (closed) 2006 { 2007 close(); 2008 } 2009 } 2010 2011 2012 2013 /** 2014 * Indicates that the provided connection should be removed from the pool, 2015 * and that no new connection should be created to take its place. This may 2016 * be used to shrink the pool if such functionality is desired. 2017 * 2018 * @param connection The connection to be discarded. 2019 */ 2020 public void discardConnection(final LDAPConnection connection) 2021 { 2022 if (connection == null) 2023 { 2024 return; 2025 } 2026 2027 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2028 null, null); 2029 connection.terminate(null); 2030 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2031 2032 if (availableConnections.remainingCapacity() > 0) 2033 { 2034 final int newReplaceCount = failedReplaceCount.incrementAndGet(); 2035 if (newReplaceCount > numConnections) 2036 { 2037 failedReplaceCount.set(numConnections); 2038 } 2039 } 2040 } 2041 2042 2043 2044 /** 2045 * Performs a bind on the provided connection before releasing it back to the 2046 * pool, so that it will be authenticated as the same user as 2047 * newly-established connections. If newly-established connections are 2048 * unauthenticated, then this method will perform an anonymous simple bind to 2049 * ensure that the resulting connection is unauthenticated. 2050 * 2051 * Releases the provided connection back to this pool. 2052 * 2053 * @param connection The connection to be released back to the pool after 2054 * being re-authenticated. 2055 */ 2056 public void releaseAndReAuthenticateConnection( 2057 final LDAPConnection connection) 2058 { 2059 if (connection == null) 2060 { 2061 return; 2062 } 2063 2064 try 2065 { 2066 BindResult bindResult; 2067 try 2068 { 2069 if (bindRequest == null) 2070 { 2071 bindResult = connection.bind("", ""); 2072 } 2073 else 2074 { 2075 bindResult = connection.bind(bindRequest.duplicate()); 2076 } 2077 } 2078 catch (final LDAPBindException lbe) 2079 { 2080 Debug.debugException(lbe); 2081 bindResult = lbe.getBindResult(); 2082 } 2083 2084 try 2085 { 2086 healthCheck.ensureConnectionValidAfterAuthentication(connection, 2087 bindResult); 2088 if (bindResult.getResultCode() != ResultCode.SUCCESS) 2089 { 2090 throw new LDAPBindException(bindResult); 2091 } 2092 } 2093 catch (final LDAPException le) 2094 { 2095 Debug.debugException(le); 2096 2097 try 2098 { 2099 connection.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le); 2100 connection.setClosed(); 2101 releaseDefunctConnection(connection); 2102 } 2103 catch (final Exception e) 2104 { 2105 Debug.debugException(e); 2106 } 2107 2108 throw le; 2109 } 2110 2111 releaseConnection(connection); 2112 } 2113 catch (final Exception e) 2114 { 2115 Debug.debugException(e); 2116 releaseDefunctConnection(connection); 2117 } 2118 } 2119 2120 2121 2122 /** 2123 * {@inheritDoc} 2124 */ 2125 @Override() 2126 public void releaseDefunctConnection(final LDAPConnection connection) 2127 { 2128 if (connection == null) 2129 { 2130 return; 2131 } 2132 2133 connection.setConnectionPoolName(connectionPoolName); 2134 poolStatistics.incrementNumConnectionsClosedDefunct(); 2135 handleDefunctConnection(connection); 2136 } 2137 2138 2139 2140 /** 2141 * Performs the real work of terminating a defunct connection and replacing it 2142 * with a new connection if possible. 2143 * 2144 * @param connection The defunct connection to be replaced. 2145 * 2146 * @return The new connection created to take the place of the defunct 2147 * connection, or {@code null} if no new connection was created. 2148 * Note that if a connection is returned, it will have already been 2149 * made available and the caller must not rely on it being unused for 2150 * any other purpose. 2151 */ 2152 private LDAPConnection handleDefunctConnection( 2153 final LDAPConnection connection) 2154 { 2155 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null, 2156 null); 2157 connection.setClosed(); 2158 2159 if (closed) 2160 { 2161 return null; 2162 } 2163 2164 if (createIfNecessary && (availableConnections.remainingCapacity() <= 0)) 2165 { 2166 return null; 2167 } 2168 2169 try 2170 { 2171 final LDAPConnection conn = createConnection(); 2172 if (maxDefunctReplacementConnectionAge != null) 2173 { 2174 // Only set the maximum age if there isn't one already set for the 2175 // connection (i.e., because it was defined by the server set). 2176 if (conn.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE) == null) 2177 { 2178 conn.setAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE, 2179 maxDefunctReplacementConnectionAge); 2180 } 2181 } 2182 2183 if (! availableConnections.offer(conn)) 2184 { 2185 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2186 null, null); 2187 conn.terminate(null); 2188 return null; 2189 } 2190 2191 return conn; 2192 } 2193 catch (final LDAPException le) 2194 { 2195 Debug.debugException(le); 2196 final int newReplaceCount = failedReplaceCount.incrementAndGet(); 2197 if (newReplaceCount > numConnections) 2198 { 2199 failedReplaceCount.set(numConnections); 2200 } 2201 return null; 2202 } 2203 } 2204 2205 2206 2207 /** 2208 * {@inheritDoc} 2209 */ 2210 @Override() 2211 public LDAPConnection replaceDefunctConnection( 2212 final LDAPConnection connection) 2213 throws LDAPException 2214 { 2215 poolStatistics.incrementNumConnectionsClosedDefunct(); 2216 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null, 2217 null); 2218 connection.setClosed(); 2219 2220 if (closed) 2221 { 2222 throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get()); 2223 } 2224 2225 try 2226 { 2227 return createConnection(); 2228 } 2229 catch (final LDAPException le) 2230 { 2231 Debug.debugException(le); 2232 failedReplaceCount.incrementAndGet(); 2233 throw le; 2234 } 2235 } 2236 2237 2238 2239 /** 2240 * {@inheritDoc} 2241 */ 2242 @Override() 2243 public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections() 2244 { 2245 return retryOperationTypes.get(); 2246 } 2247 2248 2249 2250 /** 2251 * {@inheritDoc} 2252 */ 2253 @Override() 2254 public void setRetryFailedOperationsDueToInvalidConnections( 2255 final Set<OperationType> operationTypes) 2256 { 2257 if ((operationTypes == null) || operationTypes.isEmpty()) 2258 { 2259 retryOperationTypes.set( 2260 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class))); 2261 } 2262 else 2263 { 2264 final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class); 2265 s.addAll(operationTypes); 2266 retryOperationTypes.set(Collections.unmodifiableSet(s)); 2267 } 2268 } 2269 2270 2271 2272 /** 2273 * Indicates whether the provided connection should be considered expired. 2274 * 2275 * @param connection The connection for which to make the determination. 2276 * 2277 * @return {@code true} if the provided connection should be considered 2278 * expired, or {@code false} if not. 2279 */ 2280 private boolean connectionIsExpired(final LDAPConnection connection) 2281 { 2282 // There may be a custom maximum connection age for the connection. If that 2283 // is the case, then use that custom max age rather than the pool-default 2284 // max age. 2285 final long maxAge; 2286 final Object maxAgeObj = 2287 connection.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE); 2288 if ((maxAgeObj != null) && (maxAgeObj instanceof Long)) 2289 { 2290 maxAge = (Long) maxAgeObj; 2291 } 2292 else 2293 { 2294 maxAge = maxConnectionAge; 2295 } 2296 2297 // If connection expiration is not enabled, then there is nothing to do. 2298 if (maxAge <= 0L) 2299 { 2300 return false; 2301 } 2302 2303 // If there is a minimum disconnect interval, then make sure that we have 2304 // not closed another expired connection too recently. 2305 final long currentTime = System.currentTimeMillis(); 2306 if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval) 2307 { 2308 return false; 2309 } 2310 2311 // Get the age of the connection and see if it is expired. 2312 final long connectionAge = currentTime - connection.getConnectTime(); 2313 return (connectionAge > maxAge); 2314 } 2315 2316 2317 2318 /** 2319 * Specifies the bind request that will be used to authenticate subsequent new 2320 * connections that are established by this connection pool. The 2321 * authentication state for existing connections will not be altered unless 2322 * one of the {@code bindAndRevertAuthentication} or 2323 * {@code releaseAndReAuthenticateConnection} methods are invoked on those 2324 * connections. 2325 * 2326 * @param bindRequest The bind request that will be used to authenticate new 2327 * connections that are established by this pool, or 2328 * that will be applied to existing connections via the 2329 * {@code bindAndRevertAuthentication} or 2330 * {@code releaseAndReAuthenticateConnection} method. It 2331 * may be {@code null} if new connections should be 2332 * unauthenticated. 2333 */ 2334 public void setBindRequest(final BindRequest bindRequest) 2335 { 2336 this.bindRequest = bindRequest; 2337 } 2338 2339 2340 2341 /** 2342 * Specifies the server set that should be used to establish new connections 2343 * for use in this connection pool. Existing connections will not be 2344 * affected. 2345 * 2346 * @param serverSet The server set that should be used to establish new 2347 * connections for use in this connection pool. It must 2348 * not be {@code null}. 2349 */ 2350 public void setServerSet(final ServerSet serverSet) 2351 { 2352 Validator.ensureNotNull(serverSet); 2353 this.serverSet = serverSet; 2354 } 2355 2356 2357 2358 /** 2359 * {@inheritDoc} 2360 */ 2361 @Override() 2362 public String getConnectionPoolName() 2363 { 2364 return connectionPoolName; 2365 } 2366 2367 2368 2369 /** 2370 * {@inheritDoc} 2371 */ 2372 @Override() 2373 public void setConnectionPoolName(final String connectionPoolName) 2374 { 2375 this.connectionPoolName = connectionPoolName; 2376 for (final LDAPConnection c : availableConnections) 2377 { 2378 c.setConnectionPoolName(connectionPoolName); 2379 } 2380 } 2381 2382 2383 2384 /** 2385 * Indicates whether the connection pool should create a new connection if one 2386 * is requested when there are none available. 2387 * 2388 * @return {@code true} if a new connection should be created if none are 2389 * available when a request is received, or {@code false} if an 2390 * exception should be thrown to indicate that no connection is 2391 * available. 2392 */ 2393 public boolean getCreateIfNecessary() 2394 { 2395 return createIfNecessary; 2396 } 2397 2398 2399 2400 /** 2401 * Specifies whether the connection pool should create a new connection if one 2402 * is requested when there are none available. 2403 * 2404 * @param createIfNecessary Specifies whether the connection pool should 2405 * create a new connection if one is requested when 2406 * there are none available. 2407 */ 2408 public void setCreateIfNecessary(final boolean createIfNecessary) 2409 { 2410 this.createIfNecessary = createIfNecessary; 2411 } 2412 2413 2414 2415 /** 2416 * Retrieves the maximum length of time in milliseconds to wait for a 2417 * connection to become available when trying to obtain a connection from the 2418 * pool. 2419 * 2420 * @return The maximum length of time in milliseconds to wait for a 2421 * connection to become available when trying to obtain a connection 2422 * from the pool, or zero to indicate that the pool should not block 2423 * at all if no connections are available and that it should either 2424 * create a new connection or throw an exception. 2425 */ 2426 public long getMaxWaitTimeMillis() 2427 { 2428 return maxWaitTime; 2429 } 2430 2431 2432 2433 /** 2434 * Specifies the maximum length of time in milliseconds to wait for a 2435 * connection to become available when trying to obtain a connection from the 2436 * pool. 2437 * 2438 * @param maxWaitTime The maximum length of time in milliseconds to wait for 2439 * a connection to become available when trying to obtain 2440 * a connection from the pool. A value of zero should be 2441 * used to indicate that the pool should not block at all 2442 * if no connections are available and that it should 2443 * either create a new connection or throw an exception. 2444 */ 2445 public void setMaxWaitTimeMillis(final long maxWaitTime) 2446 { 2447 if (maxWaitTime > 0L) 2448 { 2449 this.maxWaitTime = maxWaitTime; 2450 } 2451 else 2452 { 2453 this.maxWaitTime = 0L; 2454 } 2455 } 2456 2457 2458 2459 /** 2460 * Retrieves the maximum length of time in milliseconds that a connection in 2461 * this pool may be established before it is closed and replaced with another 2462 * connection. 2463 * 2464 * @return The maximum length of time in milliseconds that a connection in 2465 * this pool may be established before it is closed and replaced with 2466 * another connection, or {@code 0L} if no maximum age should be 2467 * enforced. 2468 */ 2469 public long getMaxConnectionAgeMillis() 2470 { 2471 return maxConnectionAge; 2472 } 2473 2474 2475 2476 /** 2477 * Specifies the maximum length of time in milliseconds that a connection in 2478 * this pool may be established before it should be closed and replaced with 2479 * another connection. 2480 * 2481 * @param maxConnectionAge The maximum length of time in milliseconds that a 2482 * connection in this pool may be established before 2483 * it should be closed and replaced with another 2484 * connection. A value of zero indicates that no 2485 * maximum age should be enforced. 2486 */ 2487 public void setMaxConnectionAgeMillis(final long maxConnectionAge) 2488 { 2489 if (maxConnectionAge > 0L) 2490 { 2491 this.maxConnectionAge = maxConnectionAge; 2492 } 2493 else 2494 { 2495 this.maxConnectionAge = 0L; 2496 } 2497 } 2498 2499 2500 2501 /** 2502 * Retrieves the maximum connection age that should be used for connections 2503 * that were created in order to replace defunct connections. It is possible 2504 * to define a custom maximum connection age for these connections to allow 2505 * them to be closed and re-established more quickly to allow for a 2506 * potentially quicker fail-back to a normal state. Note, that if this 2507 * capability is to be used, then the maximum age for these connections should 2508 * be long enough to allow the problematic server to become available again 2509 * under normal circumstances (e.g., it should be long enough for at least a 2510 * shutdown and restart of the server, plus some overhead for potentially 2511 * performing routine maintenance while the server is offline, or a chance for 2512 * an administrator to be made available that a server has gone down). 2513 * 2514 * @return The maximum connection age that should be used for connections 2515 * that were created in order to replace defunct connections, a value 2516 * of zero to indicate that no maximum age should be enforced, or 2517 * {@code null} if the value returned by the 2518 * {@link #getMaxConnectionAgeMillis()} method should be used. 2519 */ 2520 public Long getMaxDefunctReplacementConnectionAgeMillis() 2521 { 2522 return maxDefunctReplacementConnectionAge; 2523 } 2524 2525 2526 2527 /** 2528 * Specifies the maximum connection age that should be used for connections 2529 * that were created in order to replace defunct connections. It is possible 2530 * to define a custom maximum connection age for these connections to allow 2531 * them to be closed and re-established more quickly to allow for a 2532 * potentially quicker fail-back to a normal state. Note, that if this 2533 * capability is to be used, then the maximum age for these connections should 2534 * be long enough to allow the problematic server to become available again 2535 * under normal circumstances (e.g., it should be long enough for at least a 2536 * shutdown and restart of the server, plus some overhead for potentially 2537 * performing routine maintenance while the server is offline, or a chance for 2538 * an administrator to be made available that a server has gone down). 2539 * 2540 * @param maxDefunctReplacementConnectionAge The maximum connection age that 2541 * should be used for connections that were created in order to 2542 * replace defunct connections. It may be zero if no maximum age 2543 * should be enforced for such connections, or it may be 2544 * {@code null} if the value returned by the 2545 * {@link #getMaxConnectionAgeMillis()} method should be used. 2546 */ 2547 public void setMaxDefunctReplacementConnectionAgeMillis( 2548 final Long maxDefunctReplacementConnectionAge) 2549 { 2550 if (maxDefunctReplacementConnectionAge == null) 2551 { 2552 this.maxDefunctReplacementConnectionAge = null; 2553 } 2554 else if (maxDefunctReplacementConnectionAge > 0L) 2555 { 2556 this.maxDefunctReplacementConnectionAge = 2557 maxDefunctReplacementConnectionAge; 2558 } 2559 else 2560 { 2561 this.maxDefunctReplacementConnectionAge = 0L; 2562 } 2563 } 2564 2565 2566 2567 /** 2568 * Indicates whether to check the age of a connection against the configured 2569 * maximum connection age whenever it is released to the pool. By default, 2570 * connection age is evaluated in the background using the health check 2571 * thread, but it is also possible to configure the pool to additionally 2572 * examine the age of a connection when it is returned to the pool. 2573 * <BR><BR> 2574 * Performing connection age evaluation only in the background will ensure 2575 * that connections are only closed and re-established in a single-threaded 2576 * manner, which helps minimize the load against the target server, but only 2577 * checks connections that are not in use when the health check thread is 2578 * active. If the pool is configured to also evaluate the connection age when 2579 * connections are returned to the pool, then it may help ensure that the 2580 * maximum connection age is honored more strictly for all connections, but 2581 * in busy applications may lead to cases in which multiple connections are 2582 * closed and re-established simultaneously, which may increase load against 2583 * the directory server. The {@link #setMinDisconnectIntervalMillis(long)} 2584 * method may be used to help mitigate the potential performance impact of 2585 * closing and re-establishing multiple connections simultaneously. 2586 * 2587 * @return {@code true} if the connection pool should check connection age in 2588 * both the background health check thread and when connections are 2589 * released to the pool, or {@code false} if the connection age 2590 * should only be checked by the background health check thread. 2591 */ 2592 public boolean checkConnectionAgeOnRelease() 2593 { 2594 return checkConnectionAgeOnRelease; 2595 } 2596 2597 2598 2599 /** 2600 * Specifies whether to check the age of a connection against the configured 2601 * maximum connection age whenever it is released to the pool. By default, 2602 * connection age is evaluated in the background using the health check 2603 * thread, but it is also possible to configure the pool to additionally 2604 * examine the age of a connection when it is returned to the pool. 2605 * <BR><BR> 2606 * Performing connection age evaluation only in the background will ensure 2607 * that connections are only closed and re-established in a single-threaded 2608 * manner, which helps minimize the load against the target server, but only 2609 * checks connections that are not in use when the health check thread is 2610 * active. If the pool is configured to also evaluate the connection age when 2611 * connections are returned to the pool, then it may help ensure that the 2612 * maximum connection age is honored more strictly for all connections, but 2613 * in busy applications may lead to cases in which multiple connections are 2614 * closed and re-established simultaneously, which may increase load against 2615 * the directory server. The {@link #setMinDisconnectIntervalMillis(long)} 2616 * method may be used to help mitigate the potential performance impact of 2617 * closing and re-establishing multiple connections simultaneously. 2618 * 2619 * @param checkConnectionAgeOnRelease If {@code true}, this indicates that 2620 * the connection pool should check 2621 * connection age in both the background 2622 * health check thread and when 2623 * connections are released to the pool. 2624 * If {@code false}, this indicates that 2625 * the connection pool should check 2626 * connection age only in the background 2627 * health check thread. 2628 */ 2629 public void setCheckConnectionAgeOnRelease( 2630 final boolean checkConnectionAgeOnRelease) 2631 { 2632 this.checkConnectionAgeOnRelease = checkConnectionAgeOnRelease; 2633 } 2634 2635 2636 2637 /** 2638 * Retrieves the minimum length of time in milliseconds that should pass 2639 * between connections closed because they have been established for longer 2640 * than the maximum connection age. 2641 * 2642 * @return The minimum length of time in milliseconds that should pass 2643 * between connections closed because they have been established for 2644 * longer than the maximum connection age, or {@code 0L} if expired 2645 * connections may be closed as quickly as they are identified. 2646 */ 2647 public long getMinDisconnectIntervalMillis() 2648 { 2649 return minDisconnectInterval; 2650 } 2651 2652 2653 2654 /** 2655 * Specifies the minimum length of time in milliseconds that should pass 2656 * between connections closed because they have been established for longer 2657 * than the maximum connection age. 2658 * 2659 * @param minDisconnectInterval The minimum length of time in milliseconds 2660 * that should pass between connections closed 2661 * because they have been established for 2662 * longer than the maximum connection age. A 2663 * value less than or equal to zero indicates 2664 * that no minimum time should be enforced. 2665 */ 2666 public void setMinDisconnectIntervalMillis(final long minDisconnectInterval) 2667 { 2668 if (minDisconnectInterval > 0) 2669 { 2670 this.minDisconnectInterval = minDisconnectInterval; 2671 } 2672 else 2673 { 2674 this.minDisconnectInterval = 0L; 2675 } 2676 } 2677 2678 2679 2680 /** 2681 * {@inheritDoc} 2682 */ 2683 @Override() 2684 public LDAPConnectionPoolHealthCheck getHealthCheck() 2685 { 2686 return healthCheck; 2687 } 2688 2689 2690 2691 /** 2692 * Sets the health check implementation for this connection pool. 2693 * 2694 * @param healthCheck The health check implementation for this connection 2695 * pool. It must not be {@code null}. 2696 */ 2697 public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck) 2698 { 2699 Validator.ensureNotNull(healthCheck); 2700 this.healthCheck = healthCheck; 2701 } 2702 2703 2704 2705 /** 2706 * {@inheritDoc} 2707 */ 2708 @Override() 2709 public long getHealthCheckIntervalMillis() 2710 { 2711 return healthCheckInterval; 2712 } 2713 2714 2715 2716 /** 2717 * {@inheritDoc} 2718 */ 2719 @Override() 2720 public void setHealthCheckIntervalMillis(final long healthCheckInterval) 2721 { 2722 Validator.ensureTrue(healthCheckInterval > 0L, 2723 "LDAPConnectionPool.healthCheckInterval must be greater than 0."); 2724 this.healthCheckInterval = healthCheckInterval; 2725 healthCheckThread.wakeUp(); 2726 } 2727 2728 2729 2730 /** 2731 * Indicates whether health check processing for connections operating in 2732 * synchronous mode should include attempting to perform a read from each 2733 * connection with a very short timeout. This can help detect unsolicited 2734 * responses and unexpected connection closures in a more timely manner. This 2735 * will be ignored for connections not operating in synchronous mode. 2736 * 2737 * @return {@code true} if health check processing for connections operating 2738 * in synchronous mode should include a read attempt with a very 2739 * short timeout, or {@code false} if not. 2740 */ 2741 public boolean trySynchronousReadDuringHealthCheck() 2742 { 2743 return trySynchronousReadDuringHealthCheck; 2744 } 2745 2746 2747 2748 /** 2749 * Specifies whether health check processing for connections operating in 2750 * synchronous mode should include attempting to perform a read from each 2751 * connection with a very short timeout. 2752 * 2753 * @param trySynchronousReadDuringHealthCheck Indicates whether health check 2754 * processing for connections 2755 * operating in synchronous mode 2756 * should include attempting to 2757 * perform a read from each 2758 * connection with a very short 2759 * timeout. 2760 */ 2761 public void setTrySynchronousReadDuringHealthCheck( 2762 final boolean trySynchronousReadDuringHealthCheck) 2763 { 2764 this.trySynchronousReadDuringHealthCheck = 2765 trySynchronousReadDuringHealthCheck; 2766 } 2767 2768 2769 2770 /** 2771 * {@inheritDoc} 2772 */ 2773 @Override() 2774 protected void doHealthCheck() 2775 { 2776 invokeHealthCheck(null, true); 2777 } 2778 2779 2780 2781 /** 2782 * Invokes a synchronous one-time health-check against the connections in this 2783 * pool that are not currently in use. This will be independent of any 2784 * background health checking that may be automatically performed by the pool. 2785 * 2786 * @param healthCheck The health check to use. If this is 2787 * {@code null}, then the pool's 2788 * currently-configured health check (if any) will 2789 * be used. If this is {@code null} and there is 2790 * no health check configured for the pool, then 2791 * only a basic set of checks. 2792 * @param checkForExpiration Indicates whether to check to see if any 2793 * connections have been established for longer 2794 * than the maximum connection age. If this is 2795 * {@code true} then any expired connections will 2796 * be closed and replaced with newly-established 2797 * connections. 2798 * 2799 * @return An object with information about the result of the health check 2800 * processing. 2801 */ 2802 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck( 2803 final LDAPConnectionPoolHealthCheck healthCheck, 2804 final boolean checkForExpiration) 2805 { 2806 return invokeHealthCheck(healthCheck, checkForExpiration, 2807 checkForExpiration); 2808 } 2809 2810 2811 2812 /** 2813 * Invokes a synchronous one-time health-check against the connections in this 2814 * pool that are not currently in use. This will be independent of any 2815 * background health checking that may be automatically performed by the pool. 2816 * 2817 * @param healthCheck The health check to use. If this is 2818 * {@code null}, then the pool's 2819 * currently-configured health check (if any) 2820 * will be used. If this is {@code null} and 2821 * there is no health check configured for the 2822 * pool, then only a basic set of checks. 2823 * @param checkForExpiration Indicates whether to check to see if any 2824 * connections have been established for 2825 * longer than the maximum connection age. If 2826 * this is {@code true} then any expired 2827 * connections will be closed and replaced 2828 * with newly-established connections. 2829 * @param checkMinConnectionGoal Indicates whether to check to see if the 2830 * currently-available number of connections 2831 * is less than the minimum available 2832 * connection goal. If this is {@code true} 2833 * the minimum available connection goal is 2834 * greater than zero, and the number of 2835 * currently-available connections is less 2836 * than the goal, then this method will 2837 * attempt to create enough new connections to 2838 * reach the goal. 2839 * 2840 * @return An object with information about the result of the health check 2841 * processing. 2842 */ 2843 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck( 2844 final LDAPConnectionPoolHealthCheck healthCheck, 2845 final boolean checkForExpiration, 2846 final boolean checkMinConnectionGoal) 2847 { 2848 // Determine which health check to use. 2849 final LDAPConnectionPoolHealthCheck hc; 2850 if (healthCheck == null) 2851 { 2852 hc = this.healthCheck; 2853 } 2854 else 2855 { 2856 hc = healthCheck; 2857 } 2858 2859 2860 // Create a set used to hold connections that we've already examined. If we 2861 // encounter the same connection twice, then we know that we don't need to 2862 // do any more work. 2863 final HashSet<LDAPConnection> examinedConnections = 2864 new HashSet<>(StaticUtils.computeMapCapacity(numConnections)); 2865 int numExamined = 0; 2866 int numDefunct = 0; 2867 int numExpired = 0; 2868 2869 for (int i=0; i < numConnections; i++) 2870 { 2871 LDAPConnection conn = availableConnections.poll(); 2872 if (conn == null) 2873 { 2874 break; 2875 } 2876 else if (examinedConnections.contains(conn)) 2877 { 2878 if (! availableConnections.offer(conn)) 2879 { 2880 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2881 null, null); 2882 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2883 conn.terminate(null); 2884 } 2885 break; 2886 } 2887 2888 numExamined++; 2889 if (! conn.isConnected()) 2890 { 2891 numDefunct++; 2892 poolStatistics.incrementNumConnectionsClosedDefunct(); 2893 conn = handleDefunctConnection(conn); 2894 if (conn != null) 2895 { 2896 examinedConnections.add(conn); 2897 } 2898 } 2899 else 2900 { 2901 if (checkForExpiration && connectionIsExpired(conn)) 2902 { 2903 numExpired++; 2904 2905 try 2906 { 2907 final LDAPConnection newConnection = createConnection(); 2908 if (availableConnections.offer(newConnection)) 2909 { 2910 examinedConnections.add(newConnection); 2911 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED, 2912 null, null); 2913 conn.terminate(null); 2914 poolStatistics.incrementNumConnectionsClosedExpired(); 2915 lastExpiredDisconnectTime = System.currentTimeMillis(); 2916 continue; 2917 } 2918 else 2919 { 2920 newConnection.setDisconnectInfo( 2921 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null); 2922 newConnection.terminate(null); 2923 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2924 } 2925 } 2926 catch (final LDAPException le) 2927 { 2928 Debug.debugException(le); 2929 } 2930 } 2931 2932 2933 // If the connection is operating in synchronous mode, then try to read 2934 // a message on it using an extremely short timeout. This can help 2935 // detect a connection closure or unsolicited notification in a more 2936 // timely manner than if we had to wait for the client code to try to 2937 // use the connection. 2938 if (trySynchronousReadDuringHealthCheck && conn.synchronousMode()) 2939 { 2940 int previousTimeout = Integer.MIN_VALUE; 2941 Socket s = null; 2942 try 2943 { 2944 s = conn.getConnectionInternals(true).getSocket(); 2945 previousTimeout = s.getSoTimeout(); 2946 InternalSDKHelper.setSoTimeout(conn, 1); 2947 2948 final LDAPResponse response = conn.readResponse(0); 2949 if (response instanceof ConnectionClosedResponse) 2950 { 2951 numDefunct++; 2952 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2953 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null); 2954 poolStatistics.incrementNumConnectionsClosedDefunct(); 2955 conn = handleDefunctConnection(conn); 2956 if (conn != null) 2957 { 2958 examinedConnections.add(conn); 2959 } 2960 continue; 2961 } 2962 else if (response instanceof ExtendedResult) 2963 { 2964 // This means we got an unsolicited response. It could be a 2965 // notice of disconnection, or it could be something else, but in 2966 // any case we'll send it to the connection's unsolicited 2967 // notification handler (if one is defined). 2968 final UnsolicitedNotificationHandler h = conn. 2969 getConnectionOptions().getUnsolicitedNotificationHandler(); 2970 if (h != null) 2971 { 2972 h.handleUnsolicitedNotification(conn, 2973 (ExtendedResult) response); 2974 } 2975 } 2976 else if (response instanceof LDAPResult) 2977 { 2978 final LDAPResult r = (LDAPResult) response; 2979 if (r.getResultCode() == ResultCode.SERVER_DOWN) 2980 { 2981 numDefunct++; 2982 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2983 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null); 2984 poolStatistics.incrementNumConnectionsClosedDefunct(); 2985 conn = handleDefunctConnection(conn); 2986 if (conn != null) 2987 { 2988 examinedConnections.add(conn); 2989 } 2990 continue; 2991 } 2992 } 2993 } 2994 catch (final LDAPException le) 2995 { 2996 if (le.getResultCode() == ResultCode.TIMEOUT) 2997 { 2998 Debug.debugException(Level.FINEST, le); 2999 } 3000 else 3001 { 3002 Debug.debugException(le); 3003 numDefunct++; 3004 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 3005 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get( 3006 StaticUtils.getExceptionMessage(le)), le); 3007 poolStatistics.incrementNumConnectionsClosedDefunct(); 3008 conn = handleDefunctConnection(conn); 3009 if (conn != null) 3010 { 3011 examinedConnections.add(conn); 3012 } 3013 continue; 3014 } 3015 } 3016 catch (final Exception e) 3017 { 3018 Debug.debugException(e); 3019 numDefunct++; 3020 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 3021 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get( 3022 StaticUtils.getExceptionMessage(e)), 3023 e); 3024 poolStatistics.incrementNumConnectionsClosedDefunct(); 3025 conn = handleDefunctConnection(conn); 3026 if (conn != null) 3027 { 3028 examinedConnections.add(conn); 3029 } 3030 continue; 3031 } 3032 finally 3033 { 3034 if (previousTimeout != Integer.MIN_VALUE) 3035 { 3036 try 3037 { 3038 if (s != null) 3039 { 3040 InternalSDKHelper.setSoTimeout(conn, previousTimeout); 3041 } 3042 } 3043 catch (final Exception e) 3044 { 3045 Debug.debugException(e); 3046 numDefunct++; 3047 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 3048 null, e); 3049 poolStatistics.incrementNumConnectionsClosedDefunct(); 3050 conn = handleDefunctConnection(conn); 3051 if (conn != null) 3052 { 3053 examinedConnections.add(conn); 3054 } 3055 continue; 3056 } 3057 } 3058 } 3059 } 3060 3061 try 3062 { 3063 hc.ensureConnectionValidForContinuedUse(conn); 3064 if (availableConnections.offer(conn)) 3065 { 3066 examinedConnections.add(conn); 3067 } 3068 else 3069 { 3070 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 3071 null, null); 3072 poolStatistics.incrementNumConnectionsClosedUnneeded(); 3073 conn.terminate(null); 3074 } 3075 } 3076 catch (final Exception e) 3077 { 3078 Debug.debugException(e); 3079 numDefunct++; 3080 poolStatistics.incrementNumConnectionsClosedDefunct(); 3081 conn = handleDefunctConnection(conn); 3082 if (conn != null) 3083 { 3084 examinedConnections.add(conn); 3085 } 3086 } 3087 } 3088 } 3089 3090 if (checkMinConnectionGoal) 3091 { 3092 try 3093 { 3094 final int neededConnections = 3095 minConnectionGoal - availableConnections.size(); 3096 for (int i=0; i < neededConnections; i++) 3097 { 3098 final LDAPConnection conn = createConnection(hc); 3099 if (! availableConnections.offer(conn)) 3100 { 3101 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 3102 null, null); 3103 poolStatistics.incrementNumConnectionsClosedUnneeded(); 3104 conn.terminate(null); 3105 break; 3106 } 3107 } 3108 } 3109 catch (final Exception e) 3110 { 3111 Debug.debugException(e); 3112 } 3113 } 3114 3115 return new LDAPConnectionPoolHealthCheckResult(numExamined, numExpired, 3116 numDefunct); 3117 } 3118 3119 3120 3121 /** 3122 * {@inheritDoc} 3123 */ 3124 @Override() 3125 public int getCurrentAvailableConnections() 3126 { 3127 return availableConnections.size(); 3128 } 3129 3130 3131 3132 /** 3133 * {@inheritDoc} 3134 */ 3135 @Override() 3136 public int getMaximumAvailableConnections() 3137 { 3138 return numConnections; 3139 } 3140 3141 3142 3143 /** 3144 * Retrieves the goal for the minimum number of available connections that the 3145 * pool should try to maintain for immediate use. If this goal is greater 3146 * than zero, then the health checking process will attempt to create enough 3147 * new connections to achieve this goal. 3148 * 3149 * @return The goal for the minimum number of available connections that the 3150 * pool should try to maintain for immediate use, or zero if it will 3151 * not try to maintain a minimum number of available connections. 3152 */ 3153 public int getMinimumAvailableConnectionGoal() 3154 { 3155 return minConnectionGoal; 3156 } 3157 3158 3159 3160 /** 3161 * Specifies the goal for the minimum number of available connections that the 3162 * pool should try to maintain for immediate use. If this goal is greater 3163 * than zero, then the health checking process will attempt to create enough 3164 * new connections to achieve this goal. 3165 * 3166 * @param goal The goal for the minimum number of available connections that 3167 * the pool should try to maintain for immediate use. A value 3168 * less than or equal to zero indicates that the pool should not 3169 * try to maintain a minimum number of available connections. 3170 */ 3171 public void setMinimumAvailableConnectionGoal(final int goal) 3172 { 3173 if (goal > numConnections) 3174 { 3175 minConnectionGoal = numConnections; 3176 } 3177 else if (goal > 0) 3178 { 3179 minConnectionGoal = goal; 3180 } 3181 else 3182 { 3183 minConnectionGoal = 0; 3184 } 3185 } 3186 3187 3188 3189 /** 3190 * {@inheritDoc} 3191 */ 3192 @Override() 3193 public LDAPConnectionPoolStatistics getConnectionPoolStatistics() 3194 { 3195 return poolStatistics; 3196 } 3197 3198 3199 3200 /** 3201 * Attempts to reduce the number of connections available for use in the pool. 3202 * Note that this will be a best-effort attempt to reach the desired number 3203 * of connections, as other threads interacting with the connection pool may 3204 * check out and/or release connections that cause the number of available 3205 * connections to fluctuate. 3206 * 3207 * @param connectionsToRetain The number of connections that should be 3208 * retained for use in the connection pool. 3209 */ 3210 public void shrinkPool(final int connectionsToRetain) 3211 { 3212 while (availableConnections.size() > connectionsToRetain) 3213 { 3214 final LDAPConnection conn; 3215 try 3216 { 3217 conn = getConnection(); 3218 } 3219 catch (final LDAPException le) 3220 { 3221 return; 3222 } 3223 3224 if (availableConnections.size() >= connectionsToRetain) 3225 { 3226 discardConnection(conn); 3227 } 3228 else 3229 { 3230 releaseConnection(conn); 3231 return; 3232 } 3233 } 3234 } 3235 3236 3237 3238 /** 3239 * Closes this connection pool in the event that it becomes unreferenced. 3240 * 3241 * @throws Throwable If an unexpected problem occurs. 3242 */ 3243 @Override() 3244 protected void finalize() 3245 throws Throwable 3246 { 3247 super.finalize(); 3248 3249 close(); 3250 } 3251 3252 3253 3254 /** 3255 * {@inheritDoc} 3256 */ 3257 @Override() 3258 public void toString(final StringBuilder buffer) 3259 { 3260 buffer.append("LDAPConnectionPool("); 3261 3262 final String name = connectionPoolName; 3263 if (name != null) 3264 { 3265 buffer.append("name='"); 3266 buffer.append(name); 3267 buffer.append("', "); 3268 } 3269 3270 buffer.append("serverSet="); 3271 serverSet.toString(buffer); 3272 buffer.append(", maxConnections="); 3273 buffer.append(numConnections); 3274 buffer.append(')'); 3275 } 3276}