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