001 /* 002 * Copyright 2007-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2008-2015 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 = serverSet.getConnection(healthCheck); 1199 c.setConnectionPool(this); 1200 1201 // Auto-reconnect must be disabled for pooled connections, so turn it off 1202 // if the associated connection options have it enabled for some reason. 1203 LDAPConnectionOptions opts = c.getConnectionOptions(); 1204 if (opts.autoReconnect()) 1205 { 1206 opts = opts.duplicate(); 1207 opts.setAutoReconnect(false); 1208 c.setConnectionOptions(opts); 1209 } 1210 1211 1212 // Invoke pre-authentication post-connect processing. 1213 if (postConnectProcessor != null) 1214 { 1215 try 1216 { 1217 postConnectProcessor.processPreAuthenticatedConnection(c); 1218 } 1219 catch (Exception e) 1220 { 1221 debugException(e); 1222 1223 try 1224 { 1225 poolStatistics.incrementNumFailedConnectionAttempts(); 1226 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e); 1227 c.terminate(null); 1228 } 1229 catch (Exception e2) 1230 { 1231 debugException(e2); 1232 } 1233 1234 if (e instanceof LDAPException) 1235 { 1236 throw ((LDAPException) e); 1237 } 1238 else 1239 { 1240 throw new LDAPException(ResultCode.CONNECT_ERROR, 1241 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e); 1242 } 1243 } 1244 } 1245 1246 1247 // Authenticate the connection if appropriate. 1248 BindResult bindResult = null; 1249 try 1250 { 1251 if (bindRequest != null) 1252 { 1253 bindResult = c.bind(bindRequest.duplicate()); 1254 } 1255 } 1256 catch (final LDAPBindException lbe) 1257 { 1258 debugException(lbe); 1259 bindResult = lbe.getBindResult(); 1260 } 1261 catch (final LDAPException le) 1262 { 1263 debugException(le); 1264 bindResult = new BindResult(le); 1265 } 1266 1267 if (bindResult != null) 1268 { 1269 try 1270 { 1271 healthCheck.ensureConnectionValidAfterAuthentication(c, bindResult); 1272 if (bindResult.getResultCode() != ResultCode.SUCCESS) 1273 { 1274 throw new LDAPBindException(bindResult); 1275 } 1276 } 1277 catch (final LDAPException le) 1278 { 1279 debugException(le); 1280 1281 try 1282 { 1283 poolStatistics.incrementNumFailedConnectionAttempts(); 1284 c.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le); 1285 c.terminate(null); 1286 } 1287 catch (final Exception e) 1288 { 1289 debugException(e); 1290 } 1291 1292 throw le; 1293 } 1294 } 1295 1296 1297 // Invoke post-authentication post-connect processing. 1298 if (postConnectProcessor != null) 1299 { 1300 try 1301 { 1302 postConnectProcessor.processPostAuthenticatedConnection(c); 1303 } 1304 catch (Exception e) 1305 { 1306 debugException(e); 1307 try 1308 { 1309 poolStatistics.incrementNumFailedConnectionAttempts(); 1310 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e); 1311 c.terminate(null); 1312 } 1313 catch (Exception e2) 1314 { 1315 debugException(e2); 1316 } 1317 1318 if (e instanceof LDAPException) 1319 { 1320 throw ((LDAPException) e); 1321 } 1322 else 1323 { 1324 throw new LDAPException(ResultCode.CONNECT_ERROR, 1325 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e); 1326 } 1327 } 1328 } 1329 1330 1331 // Get the pooled schema if appropriate. 1332 if (opts.usePooledSchema()) 1333 { 1334 final long currentTime = System.currentTimeMillis(); 1335 if ((pooledSchema == null) || (currentTime > pooledSchema.getFirst())) 1336 { 1337 try 1338 { 1339 final Schema schema = c.getSchema(); 1340 if (schema != null) 1341 { 1342 c.setCachedSchema(schema); 1343 1344 final long timeout = opts.getPooledSchemaTimeoutMillis(); 1345 if ((timeout <= 0L) || (currentTime + timeout <= 0L)) 1346 { 1347 pooledSchema = 1348 new ObjectPair<Long,Schema>(Long.MAX_VALUE, schema); 1349 } 1350 else 1351 { 1352 pooledSchema = 1353 new ObjectPair<Long,Schema>((currentTime+timeout), schema); 1354 } 1355 } 1356 } 1357 catch (final Exception e) 1358 { 1359 debugException(e); 1360 1361 // There was a problem retrieving the schema from the server, but if 1362 // we have an earlier copy then we can assume it's still valid. 1363 if (pooledSchema != null) 1364 { 1365 c.setCachedSchema(pooledSchema.getSecond()); 1366 } 1367 } 1368 } 1369 else 1370 { 1371 c.setCachedSchema(pooledSchema.getSecond()); 1372 } 1373 } 1374 1375 1376 // Finish setting up the connection. 1377 c.setConnectionPoolName(connectionPoolName); 1378 poolStatistics.incrementNumSuccessfulConnectionAttempts(); 1379 1380 return c; 1381 } 1382 1383 1384 1385 /** 1386 * {@inheritDoc} 1387 */ 1388 @Override() 1389 public void close() 1390 { 1391 close(true, 1); 1392 } 1393 1394 1395 1396 /** 1397 * {@inheritDoc} 1398 */ 1399 @Override() 1400 public void close(final boolean unbind, final int numThreads) 1401 { 1402 closed = true; 1403 healthCheckThread.stopRunning(); 1404 1405 if (numThreads > 1) 1406 { 1407 final ArrayList<LDAPConnection> connList = 1408 new ArrayList<LDAPConnection>(availableConnections.size()); 1409 availableConnections.drainTo(connList); 1410 1411 if (! connList.isEmpty()) 1412 { 1413 final ParallelPoolCloser closer = 1414 new ParallelPoolCloser(connList, unbind, numThreads); 1415 closer.closeConnections(); 1416 } 1417 } 1418 else 1419 { 1420 while (true) 1421 { 1422 final LDAPConnection conn = availableConnections.poll(); 1423 if (conn == null) 1424 { 1425 return; 1426 } 1427 else 1428 { 1429 poolStatistics.incrementNumConnectionsClosedUnneeded(); 1430 conn.setDisconnectInfo(DisconnectType.POOL_CLOSED, null, null); 1431 if (unbind) 1432 { 1433 conn.terminate(null); 1434 } 1435 else 1436 { 1437 conn.setClosed(); 1438 } 1439 } 1440 } 1441 } 1442 } 1443 1444 1445 1446 /** 1447 * {@inheritDoc} 1448 */ 1449 @Override() 1450 public boolean isClosed() 1451 { 1452 return closed; 1453 } 1454 1455 1456 1457 /** 1458 * Processes a simple bind using a connection from this connection pool, and 1459 * then reverts that authentication by re-binding as the same user used to 1460 * authenticate new connections. If new connections are unauthenticated, then 1461 * the subsequent bind will be an anonymous simple bind. This method attempts 1462 * to ensure that processing the provided bind operation does not have a 1463 * lasting impact the authentication state of the connection used to process 1464 * it. 1465 * <BR><BR> 1466 * If the second bind attempt (the one used to restore the authentication 1467 * identity) fails, the connection will be closed as defunct so that a new 1468 * connection will be created to take its place. 1469 * 1470 * @param bindDN The bind DN for the simple bind request. 1471 * @param password The password for the simple bind request. 1472 * @param controls The optional set of controls for the simple bind request. 1473 * 1474 * @return The result of processing the provided bind operation. 1475 * 1476 * @throws LDAPException If the server rejects the bind request, or if a 1477 * problem occurs while sending the request or reading 1478 * the response. 1479 */ 1480 public BindResult bindAndRevertAuthentication(final String bindDN, 1481 final String password, 1482 final Control... controls) 1483 throws LDAPException 1484 { 1485 return bindAndRevertAuthentication( 1486 new SimpleBindRequest(bindDN, password, controls)); 1487 } 1488 1489 1490 1491 /** 1492 * Processes the provided bind request using a connection from this connection 1493 * pool, and then reverts that authentication by re-binding as the same user 1494 * used to authenticate new connections. If new connections are 1495 * unauthenticated, then the subsequent bind will be an anonymous simple bind. 1496 * This method attempts to ensure that processing the provided bind operation 1497 * does not have a lasting impact the authentication state of the connection 1498 * used to process it. 1499 * <BR><BR> 1500 * If the second bind attempt (the one used to restore the authentication 1501 * identity) fails, the connection will be closed as defunct so that a new 1502 * connection will be created to take its place. 1503 * 1504 * @param bindRequest The bind request to be processed. It must not be 1505 * {@code null}. 1506 * 1507 * @return The result of processing the provided bind operation. 1508 * 1509 * @throws LDAPException If the server rejects the bind request, or if a 1510 * problem occurs while sending the request or reading 1511 * the response. 1512 */ 1513 public BindResult bindAndRevertAuthentication(final BindRequest bindRequest) 1514 throws LDAPException 1515 { 1516 LDAPConnection conn = getConnection(); 1517 1518 try 1519 { 1520 final BindResult result = conn.bind(bindRequest); 1521 releaseAndReAuthenticateConnection(conn); 1522 return result; 1523 } 1524 catch (final Throwable t) 1525 { 1526 debugException(t); 1527 1528 if (t instanceof LDAPException) 1529 { 1530 final LDAPException le = (LDAPException) t; 1531 1532 boolean shouldThrow; 1533 try 1534 { 1535 healthCheck.ensureConnectionValidAfterException(conn, le); 1536 1537 // The above call will throw an exception if the connection doesn't 1538 // seem to be valid, so if we've gotten here then we should assume 1539 // that it is valid and we will pass the exception onto the client 1540 // without retrying the operation. 1541 releaseAndReAuthenticateConnection(conn); 1542 shouldThrow = true; 1543 } 1544 catch (final Exception e) 1545 { 1546 debugException(e); 1547 1548 // This implies that the connection is not valid. If the pool is 1549 // configured to re-try bind operations on a newly-established 1550 // connection, then that will be done later in this method. 1551 // Otherwise, release the connection as defunct and pass the bind 1552 // exception onto the client. 1553 if (! getOperationTypesToRetryDueToInvalidConnections().contains( 1554 OperationType.BIND)) 1555 { 1556 releaseDefunctConnection(conn); 1557 shouldThrow = true; 1558 } 1559 else 1560 { 1561 shouldThrow = false; 1562 } 1563 } 1564 1565 if (shouldThrow) 1566 { 1567 throw le; 1568 } 1569 } 1570 else 1571 { 1572 releaseDefunctConnection(conn); 1573 throw new LDAPException(ResultCode.LOCAL_ERROR, 1574 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t); 1575 } 1576 } 1577 1578 1579 // If we've gotten here, then the bind operation should be re-tried on a 1580 // newly-established connection. 1581 conn = replaceDefunctConnection(conn); 1582 1583 try 1584 { 1585 final BindResult result = conn.bind(bindRequest); 1586 releaseAndReAuthenticateConnection(conn); 1587 return result; 1588 } 1589 catch (final Throwable t) 1590 { 1591 debugException(t); 1592 1593 if (t instanceof LDAPException) 1594 { 1595 final LDAPException le = (LDAPException) t; 1596 1597 try 1598 { 1599 healthCheck.ensureConnectionValidAfterException(conn, le); 1600 releaseAndReAuthenticateConnection(conn); 1601 } 1602 catch (final Exception e) 1603 { 1604 debugException(e); 1605 releaseDefunctConnection(conn); 1606 } 1607 1608 throw le; 1609 } 1610 else 1611 { 1612 releaseDefunctConnection(conn); 1613 throw new LDAPException(ResultCode.LOCAL_ERROR, 1614 ERR_POOL_OP_EXCEPTION.get(getExceptionMessage(t)), t); 1615 } 1616 } 1617 } 1618 1619 1620 1621 /** 1622 * {@inheritDoc} 1623 */ 1624 @Override() 1625 public LDAPConnection getConnection() 1626 throws LDAPException 1627 { 1628 if (closed) 1629 { 1630 poolStatistics.incrementNumFailedCheckouts(); 1631 throw new LDAPException(ResultCode.CONNECT_ERROR, 1632 ERR_POOL_CLOSED.get()); 1633 } 1634 1635 LDAPConnection conn = availableConnections.poll(); 1636 if (conn != null) 1637 { 1638 if (conn.isConnected()) 1639 { 1640 try 1641 { 1642 healthCheck.ensureConnectionValidForCheckout(conn); 1643 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1644 return conn; 1645 } 1646 catch (LDAPException le) 1647 { 1648 debugException(le); 1649 } 1650 } 1651 1652 poolStatistics.incrementNumConnectionsClosedDefunct(); 1653 handleDefunctConnection(conn); 1654 for (int i=0; i < numConnections; i++) 1655 { 1656 conn = availableConnections.poll(); 1657 if (conn == null) 1658 { 1659 break; 1660 } 1661 else if (conn.isConnected()) 1662 { 1663 try 1664 { 1665 healthCheck.ensureConnectionValidForCheckout(conn); 1666 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1667 return conn; 1668 } 1669 catch (LDAPException le) 1670 { 1671 debugException(le); 1672 poolStatistics.incrementNumConnectionsClosedDefunct(); 1673 handleDefunctConnection(conn); 1674 } 1675 } 1676 else 1677 { 1678 poolStatistics.incrementNumConnectionsClosedDefunct(); 1679 handleDefunctConnection(conn); 1680 } 1681 } 1682 } 1683 1684 if (failedReplaceCount.get() > 0) 1685 { 1686 final int newReplaceCount = failedReplaceCount.getAndDecrement(); 1687 if (newReplaceCount > 0) 1688 { 1689 try 1690 { 1691 conn = createConnection(); 1692 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection(); 1693 return conn; 1694 } 1695 catch (LDAPException le) 1696 { 1697 debugException(le); 1698 failedReplaceCount.incrementAndGet(); 1699 poolStatistics.incrementNumFailedCheckouts(); 1700 throw le; 1701 } 1702 } 1703 else 1704 { 1705 failedReplaceCount.incrementAndGet(); 1706 poolStatistics.incrementNumFailedCheckouts(); 1707 throw new LDAPException(ResultCode.CONNECT_ERROR, 1708 ERR_POOL_NO_CONNECTIONS.get()); 1709 } 1710 } 1711 1712 if (maxWaitTime > 0) 1713 { 1714 try 1715 { 1716 conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS); 1717 if (conn != null) 1718 { 1719 try 1720 { 1721 healthCheck.ensureConnectionValidForCheckout(conn); 1722 poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting(); 1723 return conn; 1724 } 1725 catch (LDAPException le) 1726 { 1727 debugException(le); 1728 poolStatistics.incrementNumConnectionsClosedDefunct(); 1729 handleDefunctConnection(conn); 1730 } 1731 } 1732 } 1733 catch (InterruptedException ie) 1734 { 1735 debugException(ie); 1736 } 1737 } 1738 1739 if (createIfNecessary) 1740 { 1741 try 1742 { 1743 conn = createConnection(); 1744 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection(); 1745 return conn; 1746 } 1747 catch (LDAPException le) 1748 { 1749 debugException(le); 1750 poolStatistics.incrementNumFailedCheckouts(); 1751 throw le; 1752 } 1753 } 1754 else 1755 { 1756 poolStatistics.incrementNumFailedCheckouts(); 1757 throw new LDAPException(ResultCode.CONNECT_ERROR, 1758 ERR_POOL_NO_CONNECTIONS.get()); 1759 } 1760 } 1761 1762 1763 1764 /** 1765 * Attempts to retrieve a connection from the pool that is established to the 1766 * specified server. Note that this method will only attempt to return an 1767 * existing connection that is currently available, and will not create a 1768 * connection or wait for any checked-out connections to be returned. 1769 * 1770 * @param host The address of the server to which the desired connection 1771 * should be established. This must not be {@code null}, and 1772 * this must exactly match the address provided for the initial 1773 * connection or the {@code ServerSet} used to create the pool. 1774 * @param port The port of the server to which the desired connection should 1775 * be established. 1776 * 1777 * @return A connection that is established to the specified server, or 1778 * {@code null} if there are no available connections established to 1779 * the specified server. 1780 */ 1781 public LDAPConnection getConnection(final String host, final int port) 1782 { 1783 if (closed) 1784 { 1785 poolStatistics.incrementNumFailedCheckouts(); 1786 return null; 1787 } 1788 1789 final HashSet<LDAPConnection> examinedConnections = 1790 new HashSet<LDAPConnection>(numConnections); 1791 while (true) 1792 { 1793 final LDAPConnection conn = availableConnections.poll(); 1794 if (conn == null) 1795 { 1796 poolStatistics.incrementNumFailedCheckouts(); 1797 return null; 1798 } 1799 1800 if (examinedConnections.contains(conn)) 1801 { 1802 availableConnections.offer(conn); 1803 poolStatistics.incrementNumFailedCheckouts(); 1804 return null; 1805 } 1806 1807 if (conn.getConnectedAddress().equals(host) && 1808 (port == conn.getConnectedPort())) 1809 { 1810 try 1811 { 1812 healthCheck.ensureConnectionValidForCheckout(conn); 1813 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting(); 1814 return conn; 1815 } 1816 catch (final LDAPException le) 1817 { 1818 debugException(le); 1819 poolStatistics.incrementNumConnectionsClosedDefunct(); 1820 handleDefunctConnection(conn); 1821 continue; 1822 } 1823 } 1824 1825 if (availableConnections.offer(conn)) 1826 { 1827 examinedConnections.add(conn); 1828 } 1829 } 1830 } 1831 1832 1833 1834 /** 1835 * {@inheritDoc} 1836 */ 1837 @Override() 1838 public void releaseConnection(final LDAPConnection connection) 1839 { 1840 if (connection == null) 1841 { 1842 return; 1843 } 1844 1845 connection.setConnectionPoolName(connectionPoolName); 1846 if (checkConnectionAgeOnRelease && connectionIsExpired(connection)) 1847 { 1848 try 1849 { 1850 final LDAPConnection newConnection = createConnection(); 1851 if (availableConnections.offer(newConnection)) 1852 { 1853 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED, 1854 null, null); 1855 connection.terminate(null); 1856 poolStatistics.incrementNumConnectionsClosedExpired(); 1857 lastExpiredDisconnectTime = System.currentTimeMillis(); 1858 } 1859 else 1860 { 1861 newConnection.setDisconnectInfo( 1862 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null); 1863 newConnection.terminate(null); 1864 poolStatistics.incrementNumConnectionsClosedUnneeded(); 1865 } 1866 } 1867 catch (final LDAPException le) 1868 { 1869 debugException(le); 1870 } 1871 return; 1872 } 1873 1874 try 1875 { 1876 healthCheck.ensureConnectionValidForRelease(connection); 1877 } 1878 catch (LDAPException le) 1879 { 1880 releaseDefunctConnection(connection); 1881 return; 1882 } 1883 1884 if (availableConnections.offer(connection)) 1885 { 1886 poolStatistics.incrementNumReleasedValid(); 1887 } 1888 else 1889 { 1890 // This means that the connection pool is full, which can happen if the 1891 // pool was empty when a request came in to retrieve a connection and 1892 // createIfNecessary was true. In this case, we'll just close the 1893 // connection since we don't need it any more. 1894 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 1895 null, null); 1896 poolStatistics.incrementNumConnectionsClosedUnneeded(); 1897 connection.terminate(null); 1898 return; 1899 } 1900 1901 if (closed) 1902 { 1903 close(); 1904 } 1905 } 1906 1907 1908 1909 /** 1910 * Indicates that the provided connection should be removed from the pool, 1911 * and that no new connection should be created to take its place. This may 1912 * be used to shrink the pool if such functionality is desired. 1913 * 1914 * @param connection The connection to be discarded. 1915 */ 1916 public void discardConnection(final LDAPConnection connection) 1917 { 1918 if (connection == null) 1919 { 1920 return; 1921 } 1922 1923 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 1924 null, null); 1925 connection.terminate(null); 1926 poolStatistics.incrementNumConnectionsClosedUnneeded(); 1927 1928 if (availableConnections.remainingCapacity() > 0) 1929 { 1930 final int newReplaceCount = failedReplaceCount.incrementAndGet(); 1931 if (newReplaceCount > numConnections) 1932 { 1933 failedReplaceCount.set(numConnections); 1934 } 1935 } 1936 } 1937 1938 1939 1940 /** 1941 * Performs a bind on the provided connection before releasing it back to the 1942 * pool, so that it will be authenticated as the same user as 1943 * newly-established connections. If newly-established connections are 1944 * unauthenticated, then this method will perform an anonymous simple bind to 1945 * ensure that the resulting connection is unauthenticated. 1946 * 1947 * Releases the provided connection back to this pool. 1948 * 1949 * @param connection The connection to be released back to the pool after 1950 * being re-authenticated. 1951 */ 1952 public void releaseAndReAuthenticateConnection( 1953 final LDAPConnection connection) 1954 { 1955 if (connection == null) 1956 { 1957 return; 1958 } 1959 1960 try 1961 { 1962 BindResult bindResult; 1963 try 1964 { 1965 if (bindRequest == null) 1966 { 1967 bindResult = connection.bind("", ""); 1968 } 1969 else 1970 { 1971 bindResult = connection.bind(bindRequest.duplicate()); 1972 } 1973 } 1974 catch (final LDAPBindException lbe) 1975 { 1976 debugException(lbe); 1977 bindResult = lbe.getBindResult(); 1978 } 1979 1980 try 1981 { 1982 healthCheck.ensureConnectionValidAfterAuthentication(connection, 1983 bindResult); 1984 if (bindResult.getResultCode() != ResultCode.SUCCESS) 1985 { 1986 throw new LDAPBindException(bindResult); 1987 } 1988 } 1989 catch (final LDAPException le) 1990 { 1991 debugException(le); 1992 1993 try 1994 { 1995 connection.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le); 1996 connection.terminate(null); 1997 releaseDefunctConnection(connection); 1998 } 1999 catch (final Exception e) 2000 { 2001 debugException(e); 2002 } 2003 2004 throw le; 2005 } 2006 2007 releaseConnection(connection); 2008 } 2009 catch (final Exception e) 2010 { 2011 debugException(e); 2012 releaseDefunctConnection(connection); 2013 } 2014 } 2015 2016 2017 2018 /** 2019 * {@inheritDoc} 2020 */ 2021 @Override() 2022 public void releaseDefunctConnection(final LDAPConnection connection) 2023 { 2024 if (connection == null) 2025 { 2026 return; 2027 } 2028 2029 connection.setConnectionPoolName(connectionPoolName); 2030 poolStatistics.incrementNumConnectionsClosedDefunct(); 2031 handleDefunctConnection(connection); 2032 } 2033 2034 2035 2036 /** 2037 * Performs the real work of terminating a defunct connection and replacing it 2038 * with a new connection if possible. 2039 * 2040 * @param connection The defunct connection to be replaced. 2041 * 2042 * @return The new connection created to take the place of the defunct 2043 * connection, or {@code null} if no new connection was created. 2044 * Note that if a connection is returned, it will have already been 2045 * made available and the caller must not rely on it being unused for 2046 * any other purpose. 2047 */ 2048 private LDAPConnection handleDefunctConnection( 2049 final LDAPConnection connection) 2050 { 2051 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null, 2052 null); 2053 connection.terminate(null); 2054 2055 if (closed) 2056 { 2057 return null; 2058 } 2059 2060 if (createIfNecessary && (availableConnections.remainingCapacity() <= 0)) 2061 { 2062 return null; 2063 } 2064 2065 try 2066 { 2067 final LDAPConnection conn = createConnection(); 2068 if (maxDefunctReplacementConnectionAge != null) 2069 { 2070 // Only set the maximum age if there isn't one already set for the 2071 // connection (i.e., because it was defined by the server set). 2072 if (conn.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE) == null) 2073 { 2074 conn.setAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE, 2075 maxDefunctReplacementConnectionAge); 2076 } 2077 } 2078 2079 if (! availableConnections.offer(conn)) 2080 { 2081 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2082 null, null); 2083 conn.terminate(null); 2084 return null; 2085 } 2086 2087 return conn; 2088 } 2089 catch (LDAPException le) 2090 { 2091 debugException(le); 2092 final int newReplaceCount = failedReplaceCount.incrementAndGet(); 2093 if (newReplaceCount > numConnections) 2094 { 2095 failedReplaceCount.set(numConnections); 2096 } 2097 return null; 2098 } 2099 } 2100 2101 2102 2103 /** 2104 * {@inheritDoc} 2105 */ 2106 @Override() 2107 public LDAPConnection replaceDefunctConnection( 2108 final LDAPConnection connection) 2109 throws LDAPException 2110 { 2111 poolStatistics.incrementNumConnectionsClosedDefunct(); 2112 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null, 2113 null); 2114 connection.terminate(null); 2115 2116 if (closed) 2117 { 2118 throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get()); 2119 } 2120 2121 return createConnection(); 2122 } 2123 2124 2125 2126 /** 2127 * {@inheritDoc} 2128 */ 2129 @Override() 2130 public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections() 2131 { 2132 return retryOperationTypes.get(); 2133 } 2134 2135 2136 2137 /** 2138 * {@inheritDoc} 2139 */ 2140 @Override() 2141 public void setRetryFailedOperationsDueToInvalidConnections( 2142 final Set<OperationType> operationTypes) 2143 { 2144 if ((operationTypes == null) || operationTypes.isEmpty()) 2145 { 2146 retryOperationTypes.set( 2147 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class))); 2148 } 2149 else 2150 { 2151 final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class); 2152 s.addAll(operationTypes); 2153 retryOperationTypes.set(Collections.unmodifiableSet(s)); 2154 } 2155 } 2156 2157 2158 2159 /** 2160 * Indicates whether the provided connection should be considered expired. 2161 * 2162 * @param connection The connection for which to make the determination. 2163 * 2164 * @return {@code true} if the provided connection should be considered 2165 * expired, or {@code false} if not. 2166 */ 2167 private boolean connectionIsExpired(final LDAPConnection connection) 2168 { 2169 // There may be a custom maximum connection age for the connection. If that 2170 // is the case, then use that custom max age rather than the pool-default 2171 // max age. 2172 final long maxAge; 2173 final Object maxAgeObj = 2174 connection.getAttachment(ATTACHMENT_NAME_MAX_CONNECTION_AGE); 2175 if ((maxAgeObj != null) && (maxAgeObj instanceof Long)) 2176 { 2177 maxAge = (Long) maxAgeObj; 2178 } 2179 else 2180 { 2181 maxAge = maxConnectionAge; 2182 } 2183 2184 // If connection expiration is not enabled, then there is nothing to do. 2185 if (maxAge <= 0L) 2186 { 2187 return false; 2188 } 2189 2190 // If there is a minimum disconnect interval, then make sure that we have 2191 // not closed another expired connection too recently. 2192 final long currentTime = System.currentTimeMillis(); 2193 if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval) 2194 { 2195 return false; 2196 } 2197 2198 // Get the age of the connection and see if it is expired. 2199 final long connectionAge = currentTime - connection.getConnectTime(); 2200 return (connectionAge > maxAge); 2201 } 2202 2203 2204 2205 /** 2206 * {@inheritDoc} 2207 */ 2208 @Override() 2209 public String getConnectionPoolName() 2210 { 2211 return connectionPoolName; 2212 } 2213 2214 2215 2216 /** 2217 * {@inheritDoc} 2218 */ 2219 @Override() 2220 public void setConnectionPoolName(final String connectionPoolName) 2221 { 2222 this.connectionPoolName = connectionPoolName; 2223 for (final LDAPConnection c : availableConnections) 2224 { 2225 c.setConnectionPoolName(connectionPoolName); 2226 } 2227 } 2228 2229 2230 2231 /** 2232 * Indicates whether the connection pool should create a new connection if one 2233 * is requested when there are none available. 2234 * 2235 * @return {@code true} if a new connection should be created if none are 2236 * available when a request is received, or {@code false} if an 2237 * exception should be thrown to indicate that no connection is 2238 * available. 2239 */ 2240 public boolean getCreateIfNecessary() 2241 { 2242 return createIfNecessary; 2243 } 2244 2245 2246 2247 /** 2248 * Specifies whether the connection pool should create a new connection if one 2249 * is requested when there are none available. 2250 * 2251 * @param createIfNecessary Specifies whether the connection pool should 2252 * create a new connection if one is requested when 2253 * there are none available. 2254 */ 2255 public void setCreateIfNecessary(final boolean createIfNecessary) 2256 { 2257 this.createIfNecessary = createIfNecessary; 2258 } 2259 2260 2261 2262 /** 2263 * Retrieves the maximum length of time in milliseconds to wait for a 2264 * connection to become available when trying to obtain a connection from the 2265 * pool. 2266 * 2267 * @return The maximum length of time in milliseconds to wait for a 2268 * connection to become available when trying to obtain a connection 2269 * from the pool, or zero to indicate that the pool should not block 2270 * at all if no connections are available and that it should either 2271 * create a new connection or throw an exception. 2272 */ 2273 public long getMaxWaitTimeMillis() 2274 { 2275 return maxWaitTime; 2276 } 2277 2278 2279 2280 /** 2281 * Specifies the maximum length of time in milliseconds to wait for a 2282 * connection to become available when trying to obtain a connection from the 2283 * pool. 2284 * 2285 * @param maxWaitTime The maximum length of time in milliseconds to wait for 2286 * a connection to become available when trying to obtain 2287 * a connection from the pool. A value of zero should be 2288 * used to indicate that the pool should not block at all 2289 * if no connections are available and that it should 2290 * either create a new connection or throw an exception. 2291 */ 2292 public void setMaxWaitTimeMillis(final long maxWaitTime) 2293 { 2294 if (maxWaitTime > 0L) 2295 { 2296 this.maxWaitTime = maxWaitTime; 2297 } 2298 else 2299 { 2300 this.maxWaitTime = 0L; 2301 } 2302 } 2303 2304 2305 2306 /** 2307 * Retrieves the maximum length of time in milliseconds that a connection in 2308 * this pool may be established before it is closed and replaced with another 2309 * connection. 2310 * 2311 * @return The maximum length of time in milliseconds that a connection in 2312 * this pool may be established before it is closed and replaced with 2313 * another connection, or {@code 0L} if no maximum age should be 2314 * enforced. 2315 */ 2316 public long getMaxConnectionAgeMillis() 2317 { 2318 return maxConnectionAge; 2319 } 2320 2321 2322 2323 /** 2324 * Specifies the maximum length of time in milliseconds that a connection in 2325 * this pool may be established before it should be closed and replaced with 2326 * another connection. 2327 * 2328 * @param maxConnectionAge The maximum length of time in milliseconds that a 2329 * connection in this pool may be established before 2330 * it should be closed and replaced with another 2331 * connection. A value of zero indicates that no 2332 * maximum age should be enforced. 2333 */ 2334 public void setMaxConnectionAgeMillis(final long maxConnectionAge) 2335 { 2336 if (maxConnectionAge > 0L) 2337 { 2338 this.maxConnectionAge = maxConnectionAge; 2339 } 2340 else 2341 { 2342 this.maxConnectionAge = 0L; 2343 } 2344 } 2345 2346 2347 2348 /** 2349 * Retrieves the maximum connection age that should be used for connections 2350 * that were created in order to replace defunct connections. It is possible 2351 * to define a custom maximum connection age for these connections to allow 2352 * them to be closed and re-established more quickly to allow for a 2353 * potentially quicker fail-back to a normal state. Note, that if this 2354 * capability is to be used, then the maximum age for these connections should 2355 * be long enough to allow the problematic server to become available again 2356 * under normal circumstances (e.g., it should be long enough for at least a 2357 * shutdown and restart of the server, plus some overhead for potentially 2358 * performing routine maintenance while the server is offline, or a chance for 2359 * an administrator to be made available that a server has gone down). 2360 * 2361 * @return The maximum connection age that should be used for connections 2362 * that were created in order to replace defunct connections, a value 2363 * of zero to indicate that no maximum age should be enforced, or 2364 * {@code null} if the value returned by the 2365 * {@code getMaxConnectionAgeMillis()} method should be used. 2366 */ 2367 public Long getMaxDefunctReplacementConnectionAgeMillis() 2368 { 2369 return maxDefunctReplacementConnectionAge; 2370 } 2371 2372 2373 2374 /** 2375 * Specifies the maximum connection age that should be used for connections 2376 * that were created in order to replace defunct connections. It is possible 2377 * to define a custom maximum connection age for these connections to allow 2378 * them to be closed and re-established more quickly to allow for a 2379 * potentially quicker fail-back to a normal state. Note, that if this 2380 * capability is to be used, then the maximum age for these connections should 2381 * be long enough to allow the problematic server to become available again 2382 * under normal circumstances (e.g., it should be long enough for at least a 2383 * shutdown and restart of the server, plus some overhead for potentially 2384 * performing routine maintenance while the server is offline, or a chance for 2385 * an administrator to be made available that a server has gone down). 2386 * 2387 * @param maxDefunctReplacementConnectionAge The maximum connection age that 2388 * should be used for connections that were created in order to 2389 * replace defunct connections. It may be zero if no maximum age 2390 * should be enforced for such connections, or it may be 2391 * {@code null} if the value returned by the 2392 * {@code getMaxConnectionAgeMillis()} method should be used. 2393 */ 2394 public void setMaxDefunctReplacementConnectionAgeMillis( 2395 final Long maxDefunctReplacementConnectionAge) 2396 { 2397 if (maxDefunctReplacementConnectionAge == null) 2398 { 2399 this.maxDefunctReplacementConnectionAge = null; 2400 } 2401 else if (maxDefunctReplacementConnectionAge > 0L) 2402 { 2403 this.maxDefunctReplacementConnectionAge = 2404 maxDefunctReplacementConnectionAge; 2405 } 2406 else 2407 { 2408 this.maxDefunctReplacementConnectionAge = 0L; 2409 } 2410 } 2411 2412 2413 2414 /** 2415 * Indicates whether to check the age of a connection against the configured 2416 * maximum connection age whenever it is released to the pool. By default, 2417 * connection age is evaluated in the background using the health check 2418 * thread, but it is also possible to configure the pool to additionally 2419 * examine the age of a connection when it is returned to the pool. 2420 * <BR><BR> 2421 * Performing connection age evaluation only in the background will ensure 2422 * that connections are only closed and re-established in a single-threaded 2423 * manner, which helps minimize the load against the target server, but only 2424 * checks connections that are not in use when the health check thread is 2425 * active. If the pool is configured to also evaluate the connection age when 2426 * connections are returned to the pool, then it may help ensure that the 2427 * maximum connection age is honored more strictly for all connections, but 2428 * in busy applications may lead to cases in which multiple connections are 2429 * closed and re-established simultaneously, which may increase load against 2430 * the directory server. The {@code setMinDisconnectIntervalMillis(long)} 2431 * method may be used to help mitigate the potential performance impact of 2432 * closing and re-establishing multiple connections simultaneously. 2433 * 2434 * @return {@code true} if the connection pool should check connection age in 2435 * both the background health check thread and when connections are 2436 * released to the pool, or {@code false} if the connection age 2437 * should only be checked by the background health check thread. 2438 */ 2439 public boolean checkConnectionAgeOnRelease() 2440 { 2441 return checkConnectionAgeOnRelease; 2442 } 2443 2444 2445 2446 /** 2447 * Specifies whether to check the age of a connection against the configured 2448 * maximum connection age whenever it is released to the pool. By default, 2449 * connection age is evaluated in the background using the health check 2450 * thread, but it is also possible to configure the pool to additionally 2451 * examine the age of a connection when it is returned to the pool. 2452 * <BR><BR> 2453 * Performing connection age evaluation only in the background will ensure 2454 * that connections are only closed and re-established in a single-threaded 2455 * manner, which helps minimize the load against the target server, but only 2456 * checks connections that are not in use when the health check thread is 2457 * active. If the pool is configured to also evaluate the connection age when 2458 * connections are returned to the pool, then it may help ensure that the 2459 * maximum connection age is honored more strictly for all connections, but 2460 * in busy applications may lead to cases in which multiple connections are 2461 * closed and re-established simultaneously, which may increase load against 2462 * the directory server. The {@code setMinDisconnectIntervalMillis(long)} 2463 * method may be used to help mitigate the potential performance impact of 2464 * closing and re-establishing multiple connections simultaneously. 2465 * 2466 * @param checkConnectionAgeOnRelease If {@code true}, this indicates that 2467 * the connection pool should check 2468 * connection age in both the background 2469 * health check thread and when 2470 * connections are released to the pool. 2471 * If {@code false}, this indicates that 2472 * the connection pool should check 2473 * connection age only in the background 2474 * health check thread. 2475 */ 2476 public void setCheckConnectionAgeOnRelease( 2477 final boolean checkConnectionAgeOnRelease) 2478 { 2479 this.checkConnectionAgeOnRelease = checkConnectionAgeOnRelease; 2480 } 2481 2482 2483 2484 /** 2485 * Retrieves the minimum length of time in milliseconds that should pass 2486 * between connections closed because they have been established for longer 2487 * than the maximum connection age. 2488 * 2489 * @return The minimum length of time in milliseconds that should pass 2490 * between connections closed because they have been established for 2491 * longer than the maximum connection age, or {@code 0L} if expired 2492 * connections may be closed as quickly as they are identified. 2493 */ 2494 public long getMinDisconnectIntervalMillis() 2495 { 2496 return minDisconnectInterval; 2497 } 2498 2499 2500 2501 /** 2502 * Specifies the minimum length of time in milliseconds that should pass 2503 * between connections closed because they have been established for longer 2504 * than the maximum connection age. 2505 * 2506 * @param minDisconnectInterval The minimum length of time in milliseconds 2507 * that should pass between connections closed 2508 * because they have been established for 2509 * longer than the maximum connection age. A 2510 * value less than or equal to zero indicates 2511 * that no minimum time should be enforced. 2512 */ 2513 public void setMinDisconnectIntervalMillis(final long minDisconnectInterval) 2514 { 2515 if (minDisconnectInterval > 0) 2516 { 2517 this.minDisconnectInterval = minDisconnectInterval; 2518 } 2519 else 2520 { 2521 this.minDisconnectInterval = 0L; 2522 } 2523 } 2524 2525 2526 2527 /** 2528 * {@inheritDoc} 2529 */ 2530 @Override() 2531 public LDAPConnectionPoolHealthCheck getHealthCheck() 2532 { 2533 return healthCheck; 2534 } 2535 2536 2537 2538 /** 2539 * Sets the health check implementation for this connection pool. 2540 * 2541 * @param healthCheck The health check implementation for this connection 2542 * pool. It must not be {@code null}. 2543 */ 2544 public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck) 2545 { 2546 ensureNotNull(healthCheck); 2547 this.healthCheck = healthCheck; 2548 } 2549 2550 2551 2552 /** 2553 * {@inheritDoc} 2554 */ 2555 @Override() 2556 public long getHealthCheckIntervalMillis() 2557 { 2558 return healthCheckInterval; 2559 } 2560 2561 2562 2563 /** 2564 * {@inheritDoc} 2565 */ 2566 @Override() 2567 public void setHealthCheckIntervalMillis(final long healthCheckInterval) 2568 { 2569 ensureTrue(healthCheckInterval > 0L, 2570 "LDAPConnectionPool.healthCheckInterval must be greater than 0."); 2571 this.healthCheckInterval = healthCheckInterval; 2572 healthCheckThread.wakeUp(); 2573 } 2574 2575 2576 2577 /** 2578 * Indicates whether health check processing for connections operating in 2579 * synchronous mode should include attempting to perform a read from each 2580 * connection with a very short timeout. This can help detect unsolicited 2581 * responses and unexpected connection closures in a more timely manner. This 2582 * will be ignored for connections not operating in synchronous mode. 2583 * 2584 * @return {@code true} if health check processing for connections operating 2585 * in synchronous mode should include a read attempt with a very 2586 * short timeout, or {@code false} if not. 2587 */ 2588 public boolean trySynchronousReadDuringHealthCheck() 2589 { 2590 return trySynchronousReadDuringHealthCheck; 2591 } 2592 2593 2594 2595 /** 2596 * Specifies whether health check processing for connections operating in 2597 * synchronous mode should include attempting to perform a read from each 2598 * connection with a very short timeout. 2599 * 2600 * @param trySynchronousReadDuringHealthCheck Indicates whether health check 2601 * processing for connections 2602 * operating in synchronous mode 2603 * should include attempting to 2604 * perform a read from each 2605 * connection with a very short 2606 * timeout. 2607 */ 2608 public void setTrySynchronousReadDuringHealthCheck( 2609 final boolean trySynchronousReadDuringHealthCheck) 2610 { 2611 this.trySynchronousReadDuringHealthCheck = 2612 trySynchronousReadDuringHealthCheck; 2613 } 2614 2615 2616 2617 /** 2618 * {@inheritDoc} 2619 */ 2620 @Override() 2621 protected void doHealthCheck() 2622 { 2623 invokeHealthCheck(null, true); 2624 } 2625 2626 2627 2628 /** 2629 * Invokes a synchronous one-time health-check against the connections in this 2630 * pool that are not currently in use. This will be independent of any 2631 * background health checking that may be automatically performed by the pool. 2632 * 2633 * @param healthCheck The health check to use. If this is 2634 * {@code null}, then the pool's 2635 * currently-configured health check (if any) will 2636 * be used. If this is {@code null} and there is 2637 * no health check configured for the pool, then 2638 * only a basic set of checks. 2639 * @param checkForExpiration Indicates whether to check to see if any 2640 * connections have been established for longer 2641 * than the maximum connection age. If this is 2642 * {@code true} then any expired connections will 2643 * be closed and replaced with newly-established 2644 * connections. 2645 * 2646 * @return An object with information about the result of the health check 2647 * processing. 2648 */ 2649 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck( 2650 final LDAPConnectionPoolHealthCheck healthCheck, 2651 final boolean checkForExpiration) 2652 { 2653 return invokeHealthCheck(healthCheck, checkForExpiration, 2654 checkForExpiration); 2655 } 2656 2657 2658 2659 /** 2660 * Invokes a synchronous one-time health-check against the connections in this 2661 * pool that are not currently in use. This will be independent of any 2662 * background health checking that may be automatically performed by the pool. 2663 * 2664 * @param healthCheck The health check to use. If this is 2665 * {@code null}, then the pool's 2666 * currently-configured health check (if any) 2667 * will be used. If this is {@code null} and 2668 * there is no health check configured for the 2669 * pool, then only a basic set of checks. 2670 * @param checkForExpiration Indicates whether to check to see if any 2671 * connections have been established for 2672 * longer than the maximum connection age. If 2673 * this is {@code true} then any expired 2674 * connections will be closed and replaced 2675 * with newly-established connections. 2676 * @param checkMinConnectionGoal Indicates whether to check to see if the 2677 * currently-available number of connections 2678 * is less than the minimum available 2679 * connection goal. If this is {@code true} 2680 * the minimum available connection goal is 2681 * greater than zero, and the number of 2682 * currently-available connections is less 2683 * than the goal, then this method will 2684 * attempt to create enough new connections to 2685 * reach the goal. 2686 * 2687 * @return An object with information about the result of the health check 2688 * processing. 2689 */ 2690 public LDAPConnectionPoolHealthCheckResult invokeHealthCheck( 2691 final LDAPConnectionPoolHealthCheck healthCheck, 2692 final boolean checkForExpiration, 2693 final boolean checkMinConnectionGoal) 2694 { 2695 // Determine which health check to use. 2696 final LDAPConnectionPoolHealthCheck hc; 2697 if (healthCheck == null) 2698 { 2699 hc = this.healthCheck; 2700 } 2701 else 2702 { 2703 hc = healthCheck; 2704 } 2705 2706 2707 // Create a set used to hold connections that we've already examined. If we 2708 // encounter the same connection twice, then we know that we don't need to 2709 // do any more work. 2710 final HashSet<LDAPConnection> examinedConnections = 2711 new HashSet<LDAPConnection>(numConnections); 2712 int numExamined = 0; 2713 int numDefunct = 0; 2714 int numExpired = 0; 2715 2716 for (int i=0; i < numConnections; i++) 2717 { 2718 LDAPConnection conn = availableConnections.poll(); 2719 if (conn == null) 2720 { 2721 break; 2722 } 2723 else if (examinedConnections.contains(conn)) 2724 { 2725 if (! availableConnections.offer(conn)) 2726 { 2727 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2728 null, null); 2729 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2730 conn.terminate(null); 2731 } 2732 break; 2733 } 2734 2735 numExamined++; 2736 if (! conn.isConnected()) 2737 { 2738 numDefunct++; 2739 poolStatistics.incrementNumConnectionsClosedDefunct(); 2740 conn = handleDefunctConnection(conn); 2741 if (conn != null) 2742 { 2743 examinedConnections.add(conn); 2744 } 2745 } 2746 else 2747 { 2748 if (checkForExpiration && connectionIsExpired(conn)) 2749 { 2750 numExpired++; 2751 2752 try 2753 { 2754 final LDAPConnection newConnection = createConnection(); 2755 if (availableConnections.offer(newConnection)) 2756 { 2757 examinedConnections.add(newConnection); 2758 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED, 2759 null, null); 2760 conn.terminate(null); 2761 poolStatistics.incrementNumConnectionsClosedExpired(); 2762 lastExpiredDisconnectTime = System.currentTimeMillis(); 2763 continue; 2764 } 2765 else 2766 { 2767 newConnection.setDisconnectInfo( 2768 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null); 2769 newConnection.terminate(null); 2770 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2771 } 2772 } 2773 catch (final LDAPException le) 2774 { 2775 debugException(le); 2776 } 2777 } 2778 2779 2780 // If the connection is operating in synchronous mode, then try to read 2781 // a message on it using an extremely short timeout. This can help 2782 // detect a connection closure or unsolicited notification in a more 2783 // timely manner than if we had to wait for the client code to try to 2784 // use the connection. 2785 if (trySynchronousReadDuringHealthCheck && conn.synchronousMode()) 2786 { 2787 int previousTimeout = Integer.MIN_VALUE; 2788 Socket s = null; 2789 try 2790 { 2791 s = conn.getConnectionInternals(true).getSocket(); 2792 previousTimeout = s.getSoTimeout(); 2793 s.setSoTimeout(1); 2794 2795 final LDAPResponse response = conn.readResponse(0); 2796 if (response instanceof ConnectionClosedResponse) 2797 { 2798 numDefunct++; 2799 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2800 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null); 2801 poolStatistics.incrementNumConnectionsClosedDefunct(); 2802 conn = handleDefunctConnection(conn); 2803 if (conn != null) 2804 { 2805 examinedConnections.add(conn); 2806 } 2807 continue; 2808 } 2809 else if (response instanceof ExtendedResult) 2810 { 2811 // This means we got an unsolicited response. It could be a 2812 // notice of disconnection, or it could be something else, but in 2813 // any case we'll send it to the connection's unsolicited 2814 // notification handler (if one is defined). 2815 final UnsolicitedNotificationHandler h = conn. 2816 getConnectionOptions().getUnsolicitedNotificationHandler(); 2817 if (h != null) 2818 { 2819 h.handleUnsolicitedNotification(conn, 2820 (ExtendedResult) response); 2821 } 2822 } 2823 else if (response instanceof LDAPResult) 2824 { 2825 final LDAPResult r = (LDAPResult) response; 2826 if (r.getResultCode() == ResultCode.SERVER_DOWN) 2827 { 2828 numDefunct++; 2829 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2830 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null); 2831 poolStatistics.incrementNumConnectionsClosedDefunct(); 2832 conn = handleDefunctConnection(conn); 2833 if (conn != null) 2834 { 2835 examinedConnections.add(conn); 2836 } 2837 continue; 2838 } 2839 } 2840 } 2841 catch (final LDAPException le) 2842 { 2843 if (le.getResultCode() == ResultCode.TIMEOUT) 2844 { 2845 debugException(Level.FINEST, le); 2846 } 2847 else 2848 { 2849 debugException(le); 2850 numDefunct++; 2851 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2852 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get( 2853 getExceptionMessage(le)), le); 2854 poolStatistics.incrementNumConnectionsClosedDefunct(); 2855 conn = handleDefunctConnection(conn); 2856 if (conn != null) 2857 { 2858 examinedConnections.add(conn); 2859 } 2860 continue; 2861 } 2862 } 2863 catch (final Exception e) 2864 { 2865 debugException(e); 2866 numDefunct++; 2867 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2868 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(getExceptionMessage(e)), 2869 e); 2870 poolStatistics.incrementNumConnectionsClosedDefunct(); 2871 conn = handleDefunctConnection(conn); 2872 if (conn != null) 2873 { 2874 examinedConnections.add(conn); 2875 } 2876 continue; 2877 } 2878 finally 2879 { 2880 if (previousTimeout != Integer.MIN_VALUE) 2881 { 2882 try 2883 { 2884 s.setSoTimeout(previousTimeout); 2885 } 2886 catch (final Exception e) 2887 { 2888 debugException(e); 2889 numDefunct++; 2890 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, 2891 null, e); 2892 poolStatistics.incrementNumConnectionsClosedDefunct(); 2893 conn = handleDefunctConnection(conn); 2894 if (conn != null) 2895 { 2896 examinedConnections.add(conn); 2897 } 2898 continue; 2899 } 2900 } 2901 } 2902 } 2903 2904 try 2905 { 2906 hc.ensureConnectionValidForContinuedUse(conn); 2907 if (availableConnections.offer(conn)) 2908 { 2909 examinedConnections.add(conn); 2910 } 2911 else 2912 { 2913 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2914 null, null); 2915 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2916 conn.terminate(null); 2917 } 2918 } 2919 catch (Exception e) 2920 { 2921 debugException(e); 2922 numDefunct++; 2923 poolStatistics.incrementNumConnectionsClosedDefunct(); 2924 conn = handleDefunctConnection(conn); 2925 if (conn != null) 2926 { 2927 examinedConnections.add(conn); 2928 } 2929 } 2930 } 2931 } 2932 2933 if (checkMinConnectionGoal) 2934 { 2935 try 2936 { 2937 final int neededConnections = 2938 minConnectionGoal - availableConnections.size(); 2939 for (int i=0; i < neededConnections; i++) 2940 { 2941 final LDAPConnection conn = createConnection(hc); 2942 if (! availableConnections.offer(conn)) 2943 { 2944 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED, 2945 null, null); 2946 poolStatistics.incrementNumConnectionsClosedUnneeded(); 2947 conn.terminate(null); 2948 break; 2949 } 2950 } 2951 } 2952 catch (final Exception e) 2953 { 2954 debugException(e); 2955 } 2956 } 2957 2958 return new LDAPConnectionPoolHealthCheckResult(numExamined, numExpired, 2959 numDefunct); 2960 } 2961 2962 2963 2964 /** 2965 * {@inheritDoc} 2966 */ 2967 @Override() 2968 public int getCurrentAvailableConnections() 2969 { 2970 return availableConnections.size(); 2971 } 2972 2973 2974 2975 /** 2976 * {@inheritDoc} 2977 */ 2978 @Override() 2979 public int getMaximumAvailableConnections() 2980 { 2981 return numConnections; 2982 } 2983 2984 2985 2986 /** 2987 * Retrieves the goal for the minimum number of available connections that the 2988 * pool should try to maintain for immediate use. If this goal is greater 2989 * than zero, then the health checking process will attempt to create enough 2990 * new connections to achieve this goal. 2991 * 2992 * @return The goal for the minimum number of available connections that the 2993 * pool should try to maintain for immediate use, or zero if it will 2994 * not try to maintain a minimum number of available connections. 2995 */ 2996 public int getMinimumAvailableConnectionGoal() 2997 { 2998 return minConnectionGoal; 2999 } 3000 3001 3002 3003 /** 3004 * Specifies the goal for the minimum number of available connections that the 3005 * pool should try to maintain for immediate use. If this goal is greater 3006 * than zero, then the health checking process will attempt to create enough 3007 * new connections to achieve this goal. 3008 * 3009 * @param goal The goal for the minimum number of available connections that 3010 * the pool should try to maintain for immediate use. A value 3011 * less than or equal to zero indicates that the pool should not 3012 * try to maintain a minimum number of available connections. 3013 */ 3014 public void setMinimumAvailableConnectionGoal(final int goal) 3015 { 3016 if (goal > numConnections) 3017 { 3018 minConnectionGoal = numConnections; 3019 } 3020 else if (goal > 0) 3021 { 3022 minConnectionGoal = goal; 3023 } 3024 else 3025 { 3026 minConnectionGoal = 0; 3027 } 3028 } 3029 3030 3031 3032 /** 3033 * {@inheritDoc} 3034 */ 3035 @Override() 3036 public LDAPConnectionPoolStatistics getConnectionPoolStatistics() 3037 { 3038 return poolStatistics; 3039 } 3040 3041 3042 3043 /** 3044 * Attempts to reduce the number of connections available for use in the pool. 3045 * Note that this will be a best-effort attempt to reach the desired number 3046 * of connections, as other threads interacting with the connection pool may 3047 * check out and/or release connections that cause the number of available 3048 * connections to fluctuate. 3049 * 3050 * @param connectionsToRetain The number of connections that should be 3051 * retained for use in the connection pool. 3052 */ 3053 public void shrinkPool(final int connectionsToRetain) 3054 { 3055 while (availableConnections.size() > connectionsToRetain) 3056 { 3057 final LDAPConnection conn; 3058 try 3059 { 3060 conn = getConnection(); 3061 } 3062 catch (final LDAPException le) 3063 { 3064 return; 3065 } 3066 3067 if (availableConnections.size() >= connectionsToRetain) 3068 { 3069 discardConnection(conn); 3070 } 3071 else 3072 { 3073 releaseConnection(conn); 3074 return; 3075 } 3076 } 3077 } 3078 3079 3080 3081 /** 3082 * Closes this connection pool in the event that it becomes unreferenced. 3083 * 3084 * @throws Throwable If an unexpected problem occurs. 3085 */ 3086 @Override() 3087 protected void finalize() 3088 throws Throwable 3089 { 3090 super.finalize(); 3091 3092 close(); 3093 } 3094 3095 3096 3097 /** 3098 * {@inheritDoc} 3099 */ 3100 @Override() 3101 public void toString(final StringBuilder buffer) 3102 { 3103 buffer.append("LDAPConnectionPool("); 3104 3105 final String name = connectionPoolName; 3106 if (name != null) 3107 { 3108 buffer.append("name='"); 3109 buffer.append(name); 3110 buffer.append("', "); 3111 } 3112 3113 buffer.append("serverSet="); 3114 serverSet.toString(buffer); 3115 buffer.append(", maxConnections="); 3116 buffer.append(numConnections); 3117 buffer.append(')'); 3118 } 3119 }