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