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