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