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