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