001 /*
002 * Copyright 2007-2012 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2012 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.net.Socket;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.EnumSet;
029 import java.util.HashSet;
030 import java.util.Set;
031 import java.util.logging.Level;
032 import java.util.concurrent.LinkedBlockingQueue;
033 import java.util.concurrent.TimeUnit;
034 import java.util.concurrent.atomic.AtomicInteger;
035 import java.util.concurrent.atomic.AtomicReference;
036
037 import com.unboundid.ldap.protocol.LDAPResponse;
038 import com.unboundid.util.ThreadSafety;
039 import com.unboundid.util.ThreadSafetyLevel;
040
041 import static com.unboundid.ldap.sdk.LDAPMessages.*;
042 import static com.unboundid.util.Debug.*;
043 import static com.unboundid.util.StaticUtils.*;
044 import static com.unboundid.util.Validator.*;
045
046
047
048 /**
049 * This class provides an implementation of an LDAP connection pool, which is a
050 * structure that can hold multiple connections established to a given server
051 * that can be reused for multiple operations rather than creating and
052 * destroying connections for each operation. This connection pool
053 * implementation provides traditional methods for checking out and releasing
054 * connections, but it also provides wrapper methods that make it easy to
055 * perform operations using pooled connections without the need to explicitly
056 * check out or release the connections.
057 * <BR><BR>
058 * Note that both the {@code LDAPConnectionPool} class and the
059 * {@link LDAPConnection} class implement the {@link LDAPInterface} interface.
060 * This is a common interface that defines a number of common methods for
061 * processing LDAP requests. This means that in many cases, an application can
062 * use an object of type {@link LDAPInterface} rather than
063 * {@link LDAPConnection}, which makes it possible to work with either a single
064 * standalone connection or with a connection pool.
065 * <BR><BR>
066 * <H2>Creating a Connection Pool</H2>
067 * An LDAP connection pool can be created from either a single
068 * {@link LDAPConnection} (for which an appropriate number of copies will be
069 * created to fill out the pool) or using a {@link ServerSet} to create
070 * connections that may span multiple servers. For example:
071 * <BR><BR>
072 * <PRE>
073 * // Create a new LDAP connection pool with ten connections established and
074 * // authenticated to the same server:
075 * LDAPConnection connection = new LDAPConnection(address, port);
076 * BindResult bindResult = connection.bind(bindDN, password);
077 * LDAPConnectionPool connectionPool = new LDAPConnectionPool(connection, 10);
078 *
079 * // Create a new LDAP connection pool with 10 connections spanning multiple
080 * // servers using a server set.
081 * RoundRobinServerSet serverSet = new RoundRobinServerSet(addresses, ports);
082 * SimpleBindRequest bindRequest = new SimpleBindRequest(bindDN, password);
083 * LDAPConnectionPool connectionPool =
084 * new LDAPConnectionPool(serverSet, bindRequest, 10);
085 * </PRE>
086 * Note that in some cases, such as when using StartTLS, it may be necessary to
087 * perform some additional processing when a new connection is created for use
088 * in the connection pool. In this case, a {@link PostConnectProcessor} should
089 * be provided to accomplish this. See the documentation for the
090 * {@link StartTLSPostConnectProcessor} class for an example that demonstrates
091 * its use for creating a connection pool with connections secured using
092 * StartTLS.
093 * <BR><BR>
094 * <H2>Processing Operations with a Connection Pool</H2>
095 * If a single operation is to be processed using a connection from the
096 * connection pool, then it can be used without the need to check out or release
097 * a connection or perform any validity checking on the connection. This can
098 * be accomplished via the {@link LDAPInterface} interface that allows a
099 * connection pool to be treated like a single connection. For example, to
100 * perform a search using a pooled connection:
101 * <PRE>
102 * SearchResult searchResult =
103 * connectionPool.search("dc=example,dc=com", SearchScope.SUB,
104 * "(uid=john.doe)");
105 * </PRE>
106 * If an application needs to process multiple operations using a single
107 * connection, then it may be beneficial to obtain a connection from the pool
108 * to use for processing those operations and then return it back to the pool
109 * when it is no longer needed. This can be done using the
110 * {@link #getConnection} and {@link #releaseConnection} methods. If during
111 * processing it is determined that the connection is no longer valid, then the
112 * connection should be released back to the pool using the
113 * {@link #releaseDefunctConnection} method, which will ensure that the
114 * connection is closed and a new connection will be established to take its
115 * place in the pool.
116 * <BR><BR>
117 * Note that it is also possible to process multiple operations on a single
118 * connection using the {@link #processRequests} method. This may be useful if
119 * a fixed set of operations should be processed over the same connection and
120 * none of the subsequent requests depend upon the results of the earlier
121 * operations.
122 * <BR><BR>
123 * Connection pools should generally not be used when performing operations that
124 * may change the state of the underlying connections. This is particularly
125 * true for bind operations and the StartTLS extended operation, but it may
126 * apply to other types of operations as well.
127 * <BR><BR>
128 * Performing a bind operation using a connection from the pool will invalidate
129 * any previous authentication on that connection, and if that connection is
130 * released back to the pool without first being re-authenticated as the
131 * original user, then subsequent operation attempts may fail or be processed in
132 * an incorrect manner. Bind operations should only be performed in a
133 * connection pool if the pool is to be used exclusively for processing binds,
134 * if the bind request is specially crafted so that it will not change the
135 * identity of the associated connection (e.g., by including the retain identity
136 * request control in the bind request if using the Commercial Edition of the
137 * LDAP SDK with an UnboundID Directory Server), or if the code using the
138 * connection pool makes sure to re-authenticate the connection as the
139 * appropriate user whenever its identity has been changed.
140 * <BR><BR>
141 * The StartTLS extended operation should never be invoked on a connection which
142 * is part of a connection pool. It is acceptable for the pool to maintain
143 * connections which have been configured with StartTLS security prior to being
144 * added to the pool (via the use of the {@link StartTLSPostConnectProcessor}).
145 */
146 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
147 public final class LDAPConnectionPool
148 extends AbstractConnectionPool
149 {
150 /**
151 * The default health check interval for this connection pool, which is set to
152 * 60000 milliseconds (60 seconds).
153 */
154 private static final long DEFAULT_HEALTH_CHECK_INTERVAL = 60000L;
155
156
157
158 // A counter used to keep track of the number of times that the pool failed to
159 // replace a defunct connection. It may also be initialized to the difference
160 // between the initial and maximum number of connections that should be
161 // included in the pool.
162 private final AtomicInteger failedReplaceCount;
163
164 // The types of operations that should be retried if they fail in a manner
165 // that may be the result of a connection that is no longer valid.
166 private final AtomicReference<Set<OperationType>> retryOperationTypes;
167
168 // Indicates whether this connection pool has been closed.
169 private volatile boolean closed;
170
171 // Indicates whether to create a new connection if necessary rather than
172 // waiting for a connection to become available.
173 private boolean createIfNecessary;
174
175 // Indicates whether health check processing for connections in synchronous
176 // mode should include attempting to read with a very short timeout to attempt
177 // to detect closures and unsolicited notifications in a more timely manner.
178 private volatile boolean trySynchronousReadDuringHealthCheck;
179
180 // The bind request to use to perform authentication whenever a new connection
181 // is established.
182 private final BindRequest bindRequest;
183
184 // The number of connections to be held in this pool.
185 private final int numConnections;
186
187 // The health check implementation that should be used for this connection
188 // pool.
189 private LDAPConnectionPoolHealthCheck healthCheck;
190
191 // The thread that will be used to perform periodic background health checks
192 // for this connection pool.
193 private final LDAPConnectionPoolHealthCheckThread healthCheckThread;
194
195 // The statistics for this connection pool.
196 private final LDAPConnectionPoolStatistics poolStatistics;
197
198 // The set of connections that are currently available for use.
199 private final LinkedBlockingQueue<LDAPConnection> availableConnections;
200
201 // The length of time in milliseconds between periodic health checks against
202 // the available connections in this pool.
203 private volatile long healthCheckInterval;
204
205 // The time that the last expired connection was closed.
206 private volatile long lastExpiredDisconnectTime;
207
208 // The maximum length of time in milliseconds that a connection should be
209 // allowed to be established before terminating and re-establishing the
210 // connection.
211 private volatile long maxConnectionAge;
212
213 // The maximum length of time in milliseconds to wait for a connection to be
214 // available.
215 private long maxWaitTime;
216
217 // The minimum length of time in milliseconds that must pass between
218 // disconnects of connections that have exceeded the maximum connection age.
219 private volatile long minDisconnectInterval;
220
221 // The post-connect processor for this connection pool, if any.
222 private final PostConnectProcessor postConnectProcessor;
223
224 // The server set to use for establishing connections for use by this pool.
225 private final ServerSet serverSet;
226
227 // The user-friendly name assigned to this connection pool.
228 private String connectionPoolName;
229
230
231
232
233 /**
234 * Creates a new LDAP connection pool with up to the specified number of
235 * connections, created as clones of the provided connection. Initially, only
236 * the provided connection will be included in the pool, but additional
237 * connections will be created as needed until the pool has reached its full
238 * capacity, at which point the create if necessary and max wait time settings
239 * will be used to determine how to behave if a connection is requested but
240 * none are available.
241 *
242 * @param connection The connection to use to provide the template for
243 * the other connections to be created. This
244 * connection will be included in the pool. It must
245 * not be {@code null}, and it must be established to
246 * the target server. It does not necessarily need to
247 * be authenticated if all connections in the pool are
248 * to be unauthenticated.
249 * @param numConnections The total number of connections that should be
250 * created in the pool. It must be greater than or
251 * equal to one.
252 *
253 * @throws LDAPException If the provided connection cannot be used to
254 * initialize the pool, or if a problem occurs while
255 * attempting to establish any of the connections. If
256 * this is thrown, then all connections associated
257 * with the pool (including the one provided as an
258 * argument) will be closed.
259 */
260 public LDAPConnectionPool(final LDAPConnection connection,
261 final int numConnections)
262 throws LDAPException
263 {
264 this(connection, 1, numConnections, null);
265 }
266
267
268
269 /**
270 * Creates a new LDAP connection pool with the specified number of
271 * connections, created as clones of the provided connection.
272 *
273 * @param connection The connection to use to provide the template
274 * for the other connections to be created. This
275 * connection will be included in the pool. It
276 * must not be {@code null}, and it must be
277 * established to the target server. It does not
278 * necessarily need to be authenticated if all
279 * connections in the pool are to be
280 * unauthenticated.
281 * @param initialConnections The number of connections to initially
282 * establish when the pool is created. It must be
283 * greater than or equal to one.
284 * @param maxConnections The maximum number of connections that should
285 * be maintained in the pool. It must be greater
286 * than or equal to the initial number of
287 * connections.
288 *
289 * @throws LDAPException If the provided connection cannot be used to
290 * initialize the pool, or if a problem occurs while
291 * attempting to establish any of the connections. If
292 * this is thrown, then all connections associated
293 * with the pool (including the one provided as an
294 * argument) will be closed.
295 */
296 public LDAPConnectionPool(final LDAPConnection connection,
297 final int initialConnections,
298 final int maxConnections)
299 throws LDAPException
300 {
301 this(connection, initialConnections, maxConnections, null);
302 }
303
304
305
306 /**
307 * Creates a new LDAP connection pool with the specified number of
308 * connections, created as clones of the provided connection.
309 *
310 * @param connection The connection to use to provide the template
311 * for the other connections to be created.
312 * This connection will be included in the pool.
313 * It must not be {@code null}, and it must be
314 * established to the target server. It does
315 * not necessarily need to be authenticated if
316 * all connections in the pool are to be
317 * unauthenticated.
318 * @param initialConnections The number of connections to initially
319 * establish when the pool is created. It must
320 * be greater than or equal to one.
321 * @param maxConnections The maximum number of connections that should
322 * be maintained in the pool. It must be
323 * greater than or equal to the initial number
324 * of connections.
325 * @param postConnectProcessor A processor that should be used to perform
326 * any post-connect processing for connections
327 * in this pool. It may be {@code null} if no
328 * special processing is needed. Note that this
329 * processing will not be invoked on the
330 * provided connection that will be used as the
331 * first connection in the pool.
332 *
333 * @throws LDAPException If the provided connection cannot be used to
334 * initialize the pool, or if a problem occurs while
335 * attempting to establish any of the connections. If
336 * this is thrown, then all connections associated
337 * with the pool (including the one provided as an
338 * argument) will be closed.
339 */
340 public LDAPConnectionPool(final LDAPConnection connection,
341 final int initialConnections,
342 final int maxConnections,
343 final PostConnectProcessor postConnectProcessor)
344 throws LDAPException
345 {
346 ensureNotNull(connection);
347 ensureTrue(initialConnections >= 1,
348 "LDAPConnectionPool.initialConnections must be at least 1.");
349 ensureTrue(maxConnections >= initialConnections,
350 "LDAPConnectionPool.initialConnections must not be greater " +
351 "than maxConnections.");
352
353 this.postConnectProcessor = postConnectProcessor;
354
355 trySynchronousReadDuringHealthCheck = true;
356 healthCheck = new LDAPConnectionPoolHealthCheck();
357 healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
358 poolStatistics = new LDAPConnectionPoolStatistics(this);
359 connectionPoolName = null;
360 retryOperationTypes = new AtomicReference<Set<OperationType>>(
361 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
362
363 if (! connection.isConnected())
364 {
365 throw new LDAPException(ResultCode.PARAM_ERROR,
366 ERR_POOL_CONN_NOT_ESTABLISHED.get());
367 }
368
369
370 serverSet = new SingleServerSet(connection.getConnectedAddress(),
371 connection.getConnectedPort(),
372 connection.getLastUsedSocketFactory(),
373 connection.getConnectionOptions());
374 bindRequest = connection.getLastBindRequest();
375
376 final ArrayList<LDAPConnection> connList =
377 new ArrayList<LDAPConnection>(initialConnections);
378 connection.setConnectionName(null);
379 connection.setConnectionPool(this);
380 connList.add(connection);
381 for (int i=1; i < initialConnections; i++)
382 {
383 try
384 {
385 connList.add(createConnection());
386 }
387 catch (LDAPException le)
388 {
389 debugException(le);
390 for (final LDAPConnection c : connList)
391 {
392 try
393 {
394 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, le);
395 c.terminate(null);
396 }
397 catch (Exception e)
398 {
399 debugException(e);
400 }
401 }
402
403 throw le;
404 }
405 }
406
407 numConnections = maxConnections;
408
409 availableConnections =
410 new LinkedBlockingQueue<LDAPConnection>(numConnections);
411 availableConnections.addAll(connList);
412
413 failedReplaceCount =
414 new AtomicInteger(maxConnections - initialConnections);
415 createIfNecessary = true;
416 maxConnectionAge = 0L;
417 minDisconnectInterval = 0L;
418 lastExpiredDisconnectTime = 0L;
419 maxWaitTime = 5000L;
420 closed = false;
421
422 healthCheckThread = new LDAPConnectionPoolHealthCheckThread(this);
423 healthCheckThread.start();
424 }
425
426
427
428 /**
429 * Creates a new LDAP connection pool with the specified number of
430 * connections, created using the provided server set. Initially, only
431 * one will be created and included in the pool, but additional connections
432 * will be created as needed until the pool has reached its full capacity, at
433 * which point the create if necessary and max wait time settings will be used
434 * to determine how to behave if a connection is requested but none are
435 * available.
436 *
437 * @param serverSet The server set to use to create the connections.
438 * It is acceptable for the server set to create the
439 * connections across multiple servers.
440 * @param bindRequest The bind request to use to authenticate the
441 * connections that are established. It may be
442 * {@code null} if no authentication should be
443 * performed on the connections.
444 * @param numConnections The total number of connections that should be
445 * created in the pool. It must be greater than or
446 * equal to one.
447 *
448 * @throws LDAPException If a problem occurs while attempting to establish
449 * any of the connections. If this is thrown, then
450 * all connections associated with the pool will be
451 * closed.
452 */
453 public LDAPConnectionPool(final ServerSet serverSet,
454 final BindRequest bindRequest,
455 final int numConnections)
456 throws LDAPException
457 {
458 this(serverSet, bindRequest, 1, numConnections, null);
459 }
460
461
462
463 /**
464 * Creates a new LDAP connection pool with the specified number of
465 * connections, created using the provided server set.
466 *
467 * @param serverSet The server set to use to create the
468 * connections. It is acceptable for the server
469 * set to create the connections across multiple
470 * servers.
471 * @param bindRequest The bind request to use to authenticate the
472 * connections that are established. It may be
473 * {@code null} if no authentication should be
474 * performed on the connections.
475 * @param initialConnections The number of connections to initially
476 * establish when the pool is created. It must be
477 * greater than or equal to zero.
478 * @param maxConnections The maximum number of connections that should
479 * be maintained in the pool. It must be greater
480 * than or equal to the initial number of
481 * connections, and must not be zero.
482 *
483 * @throws LDAPException If a problem occurs while attempting to establish
484 * any of the connections. If this is thrown, then
485 * all connections associated with the pool will be
486 * closed.
487 */
488 public LDAPConnectionPool(final ServerSet serverSet,
489 final BindRequest bindRequest,
490 final int initialConnections,
491 final int maxConnections)
492 throws LDAPException
493 {
494 this(serverSet, bindRequest, initialConnections, maxConnections, null);
495 }
496
497
498
499 /**
500 * Creates a new LDAP connection pool with the specified number of
501 * connections, created using the provided server set.
502 *
503 * @param serverSet The server set to use to create the
504 * connections. It is acceptable for the server
505 * set to create the connections across multiple
506 * servers.
507 * @param bindRequest The bind request to use to authenticate the
508 * connections that are established. It may be
509 * {@code null} if no authentication should be
510 * performed on the connections.
511 * @param initialConnections The number of connections to initially
512 * establish when the pool is created. It must
513 * be greater than or equal to zero.
514 * @param maxConnections The maximum number of connections that should
515 * be maintained in the pool. It must be
516 * greater than or equal to the initial number
517 * of connections, and must not be zero.
518 * @param postConnectProcessor A processor that should be used to perform
519 * any post-connect processing for connections
520 * in this pool. It may be {@code null} if no
521 * special processing is needed.
522 *
523 * @throws LDAPException If a problem occurs while attempting to establish
524 * any of the connections. If this is thrown, then
525 * all connections associated with the pool will be
526 * closed.
527 */
528 public LDAPConnectionPool(final ServerSet serverSet,
529 final BindRequest bindRequest,
530 final int initialConnections,
531 final int maxConnections,
532 final PostConnectProcessor postConnectProcessor)
533 throws LDAPException
534 {
535 ensureNotNull(serverSet);
536 ensureTrue(initialConnections >= 0,
537 "LDAPConnectionPool.initialConnections must be greater than " +
538 "or equal to 0.");
539 ensureTrue(maxConnections > 0,
540 "LDAPConnectionPool.maxConnections must be greater than 0.");
541 ensureTrue(maxConnections >= initialConnections,
542 "LDAPConnectionPool.initialConnections must not be greater " +
543 "than maxConnections.");
544
545 this.serverSet = serverSet;
546 this.bindRequest = bindRequest;
547 this.postConnectProcessor = postConnectProcessor;
548
549 healthCheck = new LDAPConnectionPoolHealthCheck();
550 healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
551 poolStatistics = new LDAPConnectionPoolStatistics(this);
552 connectionPoolName = null;
553 retryOperationTypes = new AtomicReference<Set<OperationType>>(
554 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
555
556 final ArrayList<LDAPConnection> connList =
557 new ArrayList<LDAPConnection>(initialConnections);
558 for (int i=0; i < initialConnections; i++)
559 {
560 try
561 {
562 connList.add(createConnection());
563 }
564 catch (LDAPException le)
565 {
566 debugException(le);
567 for (final LDAPConnection c : connList)
568 {
569 try
570 {
571 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, le);
572 c.terminate(null);
573 } catch (Exception e)
574 {
575 debugException(e);
576 }
577 }
578
579 throw le;
580 }
581 }
582
583 numConnections = maxConnections;
584
585 availableConnections =
586 new LinkedBlockingQueue<LDAPConnection>(numConnections);
587 availableConnections.addAll(connList);
588
589 failedReplaceCount =
590 new AtomicInteger(maxConnections - initialConnections);
591 createIfNecessary = true;
592 maxConnectionAge = 0L;
593 minDisconnectInterval = 0L;
594 lastExpiredDisconnectTime = 0L;
595 maxWaitTime = 5000L;
596 closed = false;
597
598 healthCheckThread = new LDAPConnectionPoolHealthCheckThread(this);
599 healthCheckThread.start();
600 }
601
602
603
604 /**
605 * Creates a new LDAP connection for use in this pool.
606 *
607 * @return A new connection created for use in this pool.
608 *
609 * @throws LDAPException If a problem occurs while attempting to establish
610 * the connection. If a connection had been created,
611 * it will be closed.
612 */
613 private LDAPConnection createConnection()
614 throws LDAPException
615 {
616 final LDAPConnection c = serverSet.getConnection(healthCheck);
617 c.setConnectionPool(this);
618
619 // Auto-reconnect must be disabled for pooled connections, so turn it off
620 // if the associated connection options have it enabled for some reason.
621 LDAPConnectionOptions opts = c.getConnectionOptions();
622 if (opts.autoReconnect())
623 {
624 opts = opts.duplicate();
625 opts.setAutoReconnect(false);
626 c.setConnectionOptions(opts);
627 }
628
629 if (postConnectProcessor != null)
630 {
631 try
632 {
633 postConnectProcessor.processPreAuthenticatedConnection(c);
634 }
635 catch (Exception e)
636 {
637 debugException(e);
638
639 try
640 {
641 poolStatistics.incrementNumFailedConnectionAttempts();
642 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
643 c.terminate(null);
644 }
645 catch (Exception e2)
646 {
647 debugException(e2);
648 }
649
650 if (e instanceof LDAPException)
651 {
652 throw ((LDAPException) e);
653 }
654 else
655 {
656 throw new LDAPException(ResultCode.CONNECT_ERROR,
657 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
658 }
659 }
660 }
661
662 try
663 {
664 if (bindRequest != null)
665 {
666 c.bind(bindRequest.duplicate());
667 }
668 }
669 catch (Exception e)
670 {
671 debugException(e);
672 try
673 {
674 poolStatistics.incrementNumFailedConnectionAttempts();
675 c.setDisconnectInfo(DisconnectType.BIND_FAILED, null, e);
676 c.terminate(null);
677 }
678 catch (Exception e2)
679 {
680 debugException(e2);
681 }
682
683 if (e instanceof LDAPException)
684 {
685 throw ((LDAPException) e);
686 }
687 else
688 {
689 throw new LDAPException(ResultCode.CONNECT_ERROR,
690 ERR_POOL_CONNECT_ERROR.get(getExceptionMessage(e)), e);
691 }
692 }
693
694 if (postConnectProcessor != null)
695 {
696 try
697 {
698 postConnectProcessor.processPostAuthenticatedConnection(c);
699 }
700 catch (Exception e)
701 {
702 debugException(e);
703 try
704 {
705 poolStatistics.incrementNumFailedConnectionAttempts();
706 c.setDisconnectInfo(DisconnectType.POOL_CREATION_FAILURE, null, e);
707 c.terminate(null);
708 }
709 catch (Exception e2)
710 {
711 debugException(e2);
712 }
713
714 if (e instanceof LDAPException)
715 {
716 throw ((LDAPException) e);
717 }
718 else
719 {
720 throw new LDAPException(ResultCode.CONNECT_ERROR,
721 ERR_POOL_POST_CONNECT_ERROR.get(getExceptionMessage(e)), e);
722 }
723 }
724 }
725
726 c.setConnectionPoolName(connectionPoolName);
727 poolStatistics.incrementNumSuccessfulConnectionAttempts();
728 return c;
729 }
730
731
732
733 /**
734 * {@inheritDoc}
735 */
736 @Override()
737 public void close()
738 {
739 closed = true;
740 healthCheckThread.stopRunning();
741
742 while (true)
743 {
744 final LDAPConnection conn = availableConnections.poll();
745 if (conn == null)
746 {
747 return;
748 }
749 else
750 {
751 poolStatistics.incrementNumConnectionsClosedUnneeded();
752 conn.setDisconnectInfo(DisconnectType.POOL_CLOSED, null, null);
753 conn.terminate(null);
754 }
755 }
756 }
757
758
759
760 /**
761 * {@inheritDoc}
762 */
763 @Override()
764 public boolean isClosed()
765 {
766 return closed;
767 }
768
769
770
771 /**
772 * {@inheritDoc}
773 */
774 @Override()
775 public LDAPConnection getConnection()
776 throws LDAPException
777 {
778 if (closed)
779 {
780 poolStatistics.incrementNumFailedCheckouts();
781 throw new LDAPException(ResultCode.CONNECT_ERROR,
782 ERR_POOL_CLOSED.get());
783 }
784
785 LDAPConnection conn = availableConnections.poll();
786 if (conn != null)
787 {
788 if (conn.isConnected())
789 {
790 try
791 {
792 healthCheck.ensureConnectionValidForCheckout(conn);
793 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
794 return conn;
795 }
796 catch (LDAPException le)
797 {
798 debugException(le);
799 }
800 }
801
802 handleDefunctConnection(conn);
803 for (int i=0; i < numConnections; i++)
804 {
805 conn = availableConnections.poll();
806 if (conn == null)
807 {
808 break;
809 }
810 else if (conn.isConnected())
811 {
812 try
813 {
814 healthCheck.ensureConnectionValidForCheckout(conn);
815 poolStatistics.incrementNumSuccessfulCheckoutsWithoutWaiting();
816 return conn;
817 }
818 catch (LDAPException le)
819 {
820 debugException(le);
821 handleDefunctConnection(conn);
822 }
823 }
824 else
825 {
826 handleDefunctConnection(conn);
827 }
828 }
829 }
830
831 if (failedReplaceCount.get() > 0)
832 {
833 final int newReplaceCount = failedReplaceCount.getAndDecrement();
834 if (newReplaceCount > 0)
835 {
836 try
837 {
838 conn = createConnection();
839 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
840 return conn;
841 }
842 catch (LDAPException le)
843 {
844 debugException(le);
845 failedReplaceCount.incrementAndGet();
846 poolStatistics.incrementNumFailedCheckouts();
847 throw le;
848 }
849 }
850 else
851 {
852 failedReplaceCount.incrementAndGet();
853 poolStatistics.incrementNumFailedCheckouts();
854 throw new LDAPException(ResultCode.CONNECT_ERROR,
855 ERR_POOL_NO_CONNECTIONS.get());
856 }
857 }
858
859 if (maxWaitTime > 0)
860 {
861 try
862 {
863 conn = availableConnections.poll(maxWaitTime, TimeUnit.MILLISECONDS);
864 if (conn != null)
865 {
866 try
867 {
868 healthCheck.ensureConnectionValidForCheckout(conn);
869 poolStatistics.incrementNumSuccessfulCheckoutsAfterWaiting();
870 return conn;
871 }
872 catch (LDAPException le)
873 {
874 debugException(le);
875 handleDefunctConnection(conn);
876 }
877 }
878 }
879 catch (InterruptedException ie)
880 {
881 debugException(ie);
882 }
883 }
884
885 if (createIfNecessary)
886 {
887 try
888 {
889 conn = createConnection();
890 poolStatistics.incrementNumSuccessfulCheckoutsNewConnection();
891 return conn;
892 }
893 catch (LDAPException le)
894 {
895 debugException(le);
896 poolStatistics.incrementNumFailedCheckouts();
897 throw le;
898 }
899 }
900 else
901 {
902 poolStatistics.incrementNumFailedCheckouts();
903 throw new LDAPException(ResultCode.CONNECT_ERROR,
904 ERR_POOL_NO_CONNECTIONS.get());
905 }
906 }
907
908
909
910 /**
911 * {@inheritDoc}
912 */
913 @Override()
914 public void releaseConnection(final LDAPConnection connection)
915 {
916 if (connection == null)
917 {
918 return;
919 }
920
921 connection.setConnectionPoolName(connectionPoolName);
922 if (connectionIsExpired(connection))
923 {
924 try
925 {
926 final LDAPConnection newConnection = createConnection();
927 if (availableConnections.offer(newConnection))
928 {
929 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
930 null, null);
931 connection.terminate(null);
932 poolStatistics.incrementNumConnectionsClosedExpired();
933 lastExpiredDisconnectTime = System.currentTimeMillis();
934 return;
935 }
936 else
937 {
938 newConnection.setDisconnectInfo(
939 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
940 newConnection.terminate(null);
941 poolStatistics.incrementNumConnectionsClosedUnneeded();
942 }
943 }
944 catch (final LDAPException le)
945 {
946 debugException(le);
947 }
948 }
949
950 try
951 {
952 healthCheck.ensureConnectionValidForRelease(connection);
953 }
954 catch (LDAPException le)
955 {
956 releaseDefunctConnection(connection);
957 return;
958 }
959
960 if (availableConnections.offer(connection))
961 {
962 poolStatistics.incrementNumReleasedValid();
963 }
964 else
965 {
966 // This means that the connection pool is full, which can happen if the
967 // pool was empty when a request came in to retrieve a connection and
968 // createIfNecessary was true. In this case, we'll just close the
969 // connection since we don't need it any more.
970 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
971 null, null);
972 poolStatistics.incrementNumConnectionsClosedUnneeded();
973 connection.terminate(null);
974 return;
975 }
976
977 if (closed)
978 {
979 close();
980 }
981 }
982
983
984
985 /**
986 * {@inheritDoc}
987 */
988 @Override()
989 public void releaseDefunctConnection(final LDAPConnection connection)
990 {
991 if (connection == null)
992 {
993 return;
994 }
995
996 connection.setConnectionPoolName(connectionPoolName);
997 poolStatistics.incrementNumConnectionsClosedDefunct();
998 handleDefunctConnection(connection);
999 }
1000
1001
1002
1003 /**
1004 * Performs the real work of terminating a defunct connection and replacing it
1005 * with a new connection if possible.
1006 *
1007 * @param connection The defunct connection to be replaced.
1008 *
1009 * @return The new connection created to take the place of the defunct
1010 * connection, or {@code null} if no new connection was created.
1011 * Note that if a connection is returned, it will have already been
1012 * made available and the caller must not rely on it being unused for
1013 * any other purpose.
1014 */
1015 private LDAPConnection handleDefunctConnection(
1016 final LDAPConnection connection)
1017 {
1018 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
1019 null);
1020 connection.terminate(null);
1021
1022 if (closed)
1023 {
1024 return null;
1025 }
1026
1027 if (createIfNecessary && (availableConnections.remainingCapacity() <= 0))
1028 {
1029 return null;
1030 }
1031
1032 try
1033 {
1034 final LDAPConnection conn = createConnection();
1035 if (! availableConnections.offer(conn))
1036 {
1037 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1038 null, null);
1039 conn.terminate(null);
1040 return null;
1041 }
1042
1043 return conn;
1044 }
1045 catch (LDAPException le)
1046 {
1047 debugException(le);
1048 failedReplaceCount.incrementAndGet();
1049 return null;
1050 }
1051 }
1052
1053
1054
1055 /**
1056 * {@inheritDoc}
1057 */
1058 @Override()
1059 public LDAPConnection replaceDefunctConnection(
1060 final LDAPConnection connection)
1061 throws LDAPException
1062 {
1063 connection.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, null,
1064 null);
1065 connection.terminate(null);
1066
1067 if (closed)
1068 {
1069 throw new LDAPException(ResultCode.CONNECT_ERROR, ERR_POOL_CLOSED.get());
1070 }
1071
1072 return createConnection();
1073 }
1074
1075
1076
1077 /**
1078 * {@inheritDoc}
1079 */
1080 @Override()
1081 public Set<OperationType> getOperationTypesToRetryDueToInvalidConnections()
1082 {
1083 return retryOperationTypes.get();
1084 }
1085
1086
1087
1088 /**
1089 * {@inheritDoc}
1090 */
1091 @Override()
1092 public void setRetryFailedOperationsDueToInvalidConnections(
1093 final Set<OperationType> operationTypes)
1094 {
1095 if ((operationTypes == null) || operationTypes.isEmpty())
1096 {
1097 retryOperationTypes.set(
1098 Collections.unmodifiableSet(EnumSet.noneOf(OperationType.class)));
1099 }
1100 else
1101 {
1102 final EnumSet<OperationType> s = EnumSet.noneOf(OperationType.class);
1103 s.addAll(operationTypes);
1104 retryOperationTypes.set(Collections.unmodifiableSet(s));
1105 }
1106 }
1107
1108
1109
1110 /**
1111 * Indicates whether the provided connection should be considered expired.
1112 *
1113 * @param connection The connection for which to make the determination.
1114 *
1115 * @return {@code true} if the provided connection should be considered
1116 * expired, or {@code false} if not.
1117 */
1118 private boolean connectionIsExpired(final LDAPConnection connection)
1119 {
1120 // If connection expiration is not enabled, then there is nothing to do.
1121 if (maxConnectionAge <= 0L)
1122 {
1123 return false;
1124 }
1125
1126 // If there is a minimum disconnect interval, then make sure that we have
1127 // not closed another expired connection too recently.
1128 final long currentTime = System.currentTimeMillis();
1129 if ((currentTime - lastExpiredDisconnectTime) < minDisconnectInterval)
1130 {
1131 return false;
1132 }
1133
1134 // Get the age of the connection and see if it is expired.
1135 final long connectionAge = currentTime - connection.getConnectTime();
1136 return (connectionAge > maxConnectionAge);
1137 }
1138
1139
1140
1141 /**
1142 * {@inheritDoc}
1143 */
1144 @Override()
1145 public String getConnectionPoolName()
1146 {
1147 return connectionPoolName;
1148 }
1149
1150
1151
1152 /**
1153 * {@inheritDoc}
1154 */
1155 @Override()
1156 public void setConnectionPoolName(final String connectionPoolName)
1157 {
1158 this.connectionPoolName = connectionPoolName;
1159 for (final LDAPConnection c : availableConnections)
1160 {
1161 c.setConnectionPoolName(connectionPoolName);
1162 }
1163 }
1164
1165
1166
1167 /**
1168 * Indicates whether the connection pool should create a new connection if one
1169 * is requested when there are none available.
1170 *
1171 * @return {@code true} if a new connection should be created if none are
1172 * available when a request is received, or {@code false} if an
1173 * exception should be thrown to indicate that no connection is
1174 * available.
1175 */
1176 public boolean getCreateIfNecessary()
1177 {
1178 return createIfNecessary;
1179 }
1180
1181
1182
1183 /**
1184 * Specifies whether the connection pool should create a new connection if one
1185 * is requested when there are none available.
1186 *
1187 * @param createIfNecessary Specifies whether the connection pool should
1188 * create a new connection if one is requested when
1189 * there are none available.
1190 */
1191 public void setCreateIfNecessary(final boolean createIfNecessary)
1192 {
1193 this.createIfNecessary = createIfNecessary;
1194 }
1195
1196
1197
1198 /**
1199 * Retrieves the maximum length of time in milliseconds to wait for a
1200 * connection to become available when trying to obtain a connection from the
1201 * pool.
1202 *
1203 * @return The maximum length of time in milliseconds to wait for a
1204 * connection to become available when trying to obtain a connection
1205 * from the pool, or zero to indicate that the pool should not block
1206 * at all if no connections are available and that it should either
1207 * create a new connection or throw an exception.
1208 */
1209 public long getMaxWaitTimeMillis()
1210 {
1211 return maxWaitTime;
1212 }
1213
1214
1215
1216 /**
1217 * Specifies the maximum length of time in milliseconds to wait for a
1218 * connection to become available when trying to obtain a connection from the
1219 * pool.
1220 *
1221 * @param maxWaitTime The maximum length of time in milliseconds to wait for
1222 * a connection to become available when trying to obtain
1223 * a connection from the pool. A value of zero should be
1224 * used to indicate that the pool should not block at all
1225 * if no connections are available and that it should
1226 * either create a new connection or throw an exception.
1227 */
1228 public void setMaxWaitTimeMillis(final long maxWaitTime)
1229 {
1230 if (maxWaitTime > 0L)
1231 {
1232 this.maxWaitTime = maxWaitTime;
1233 }
1234 else
1235 {
1236 this.maxWaitTime = 0L;
1237 }
1238 }
1239
1240
1241
1242 /**
1243 * Retrieves the maximum length of time in milliseconds that a connection in
1244 * this pool may be established before it is closed and replaced with another
1245 * connection.
1246 *
1247 * @return The maximum length of time in milliseconds that a connection in
1248 * this pool may be established before it is closed and replaced with
1249 * another connection, or {@code 0L} if no maximum age should be
1250 * enforced.
1251 */
1252 public long getMaxConnectionAgeMillis()
1253 {
1254 return maxConnectionAge;
1255 }
1256
1257
1258
1259 /**
1260 * Specifies the maximum length of time in milliseconds that a connection in
1261 * this pool may be established before it should be closed and replaced with
1262 * another connection.
1263 *
1264 * @param maxConnectionAge The maximum length of time in milliseconds that a
1265 * connection in this pool may be established before
1266 * it should be closed and replaced with another
1267 * connection. A value of zero indicates that no
1268 * maximum age should be enforced.
1269 */
1270 public void setMaxConnectionAgeMillis(final long maxConnectionAge)
1271 {
1272 if (maxConnectionAge > 0L)
1273 {
1274 this.maxConnectionAge = maxConnectionAge;
1275 }
1276 else
1277 {
1278 this.maxConnectionAge = 0L;
1279 }
1280 }
1281
1282
1283
1284 /**
1285 * Retrieves the minimum length of time in milliseconds that should pass
1286 * between connections closed because they have been established for longer
1287 * than the maximum connection age.
1288 *
1289 * @return The minimum length of time in milliseconds that should pass
1290 * between connections closed because they have been established for
1291 * longer than the maximum connection age, or {@code 0L} if expired
1292 * connections may be closed as quickly as they are identified.
1293 */
1294 public long getMinDisconnectIntervalMillis()
1295 {
1296 return minDisconnectInterval;
1297 }
1298
1299
1300
1301 /**
1302 * Specifies the minimum length of time in milliseconds that should pass
1303 * between connections closed because they have been established for longer
1304 * than the maximum connection age.
1305 *
1306 * @param minDisconnectInterval The minimum length of time in milliseconds
1307 * that should pass between connections closed
1308 * because they have been established for
1309 * longer than the maximum connection age. A
1310 * value less than or equal to zero indicates
1311 * that no minimum time should be enforced.
1312 */
1313 public void setMinDisconnectIntervalMillis(final long minDisconnectInterval)
1314 {
1315 if (minDisconnectInterval > 0)
1316 {
1317 this.minDisconnectInterval = minDisconnectInterval;
1318 }
1319 else
1320 {
1321 this.minDisconnectInterval = 0L;
1322 }
1323 }
1324
1325
1326
1327 /**
1328 * {@inheritDoc}
1329 */
1330 @Override()
1331 public LDAPConnectionPoolHealthCheck getHealthCheck()
1332 {
1333 return healthCheck;
1334 }
1335
1336
1337
1338 /**
1339 * Sets the health check implementation for this connection pool.
1340 *
1341 * @param healthCheck The health check implementation for this connection
1342 * pool. It must not be {@code null}.
1343 */
1344 public void setHealthCheck(final LDAPConnectionPoolHealthCheck healthCheck)
1345 {
1346 ensureNotNull(healthCheck);
1347 this.healthCheck = healthCheck;
1348 }
1349
1350
1351
1352 /**
1353 * {@inheritDoc}
1354 */
1355 @Override()
1356 public long getHealthCheckIntervalMillis()
1357 {
1358 return healthCheckInterval;
1359 }
1360
1361
1362
1363 /**
1364 * {@inheritDoc}
1365 */
1366 @Override()
1367 public void setHealthCheckIntervalMillis(final long healthCheckInterval)
1368 {
1369 ensureTrue(healthCheckInterval > 0L,
1370 "LDAPConnectionPool.healthCheckInterval must be greater than 0.");
1371 this.healthCheckInterval = healthCheckInterval;
1372 healthCheckThread.wakeUp();
1373 }
1374
1375
1376
1377 /**
1378 * Indicates whether health check processing for connections operating in
1379 * synchronous mode should include attempting to perform a read from each
1380 * connection with a very short timeout. This can help detect unsolicited
1381 * responses and unexpected connection closures in a more timely manner. This
1382 * will be ignored for connections not operating in synchronous mode.
1383 *
1384 * @return {@code true} if health check processing for connections operating
1385 * in synchronous mode should include a read attempt with a very
1386 * short timeout, or {@code false} if not.
1387 */
1388 public boolean trySynchronousReadDuringHealthCheck()
1389 {
1390 return trySynchronousReadDuringHealthCheck;
1391 }
1392
1393
1394
1395 /**
1396 * Specifies whether health check processing for connections operating in
1397 * synchronous mode should include attempting to perform a read from each
1398 * connection with a very short timeout.
1399 *
1400 * @param trySynchronousReadDuringHealthCheck Indicates whether health check
1401 * processing for connections
1402 * operating in synchronous mode
1403 * should include attempting to
1404 * perform a read from each
1405 * connection with a very short
1406 * timeout.
1407 */
1408 public void setTrySynchronousReadDuringHealthCheck(
1409 final boolean trySynchronousReadDuringHealthCheck)
1410 {
1411 this.trySynchronousReadDuringHealthCheck =
1412 trySynchronousReadDuringHealthCheck;
1413 }
1414
1415
1416
1417 /**
1418 * {@inheritDoc}
1419 */
1420 @Override()
1421 protected void doHealthCheck()
1422 {
1423 // Create a set used to hold connections that we've already examined. If we
1424 // encounter the same connection twice, then we know that we don't need to
1425 // do any more work.
1426 final HashSet<LDAPConnection> examinedConnections =
1427 new HashSet<LDAPConnection>(numConnections);
1428
1429 for (int i=0; i < numConnections; i++)
1430 {
1431 LDAPConnection conn = availableConnections.poll();
1432 if (conn == null)
1433 {
1434 break;
1435 }
1436 else if (examinedConnections.contains(conn))
1437 {
1438 if (! availableConnections.offer(conn))
1439 {
1440 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1441 null, null);
1442 poolStatistics.incrementNumConnectionsClosedUnneeded();
1443 conn.terminate(null);
1444 }
1445 break;
1446 }
1447
1448 if (! conn.isConnected())
1449 {
1450 conn = handleDefunctConnection(conn);
1451 if (conn != null)
1452 {
1453 examinedConnections.add(conn);
1454 }
1455 }
1456 else
1457 {
1458 if (connectionIsExpired(conn))
1459 {
1460 try
1461 {
1462 final LDAPConnection newConnection = createConnection();
1463 if (availableConnections.offer(newConnection))
1464 {
1465 examinedConnections.add(newConnection);
1466 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_EXPIRED,
1467 null, null);
1468 conn.terminate(null);
1469 poolStatistics.incrementNumConnectionsClosedExpired();
1470 lastExpiredDisconnectTime = System.currentTimeMillis();
1471 continue;
1472 }
1473 else
1474 {
1475 newConnection.setDisconnectInfo(
1476 DisconnectType.POOLED_CONNECTION_UNNEEDED, null, null);
1477 newConnection.terminate(null);
1478 poolStatistics.incrementNumConnectionsClosedUnneeded();
1479 }
1480 }
1481 catch (final LDAPException le)
1482 {
1483 debugException(le);
1484 }
1485 }
1486
1487
1488 // If the connection is operating in synchronous mode, then try to read
1489 // a message on it using an extremely short timeout. This can help
1490 // detect a connection closure or unsolicited notification in a more
1491 // timely manner than if we had to wait for the client code to try to
1492 // use the connection.
1493 if (trySynchronousReadDuringHealthCheck && conn.synchronousMode())
1494 {
1495 int previousTimeout = Integer.MIN_VALUE;
1496 final Socket s = conn.getConnectionInternals().getSocket();
1497 try
1498 {
1499 previousTimeout = s.getSoTimeout();
1500 s.setSoTimeout(1);
1501
1502 final LDAPResponse response = conn.readResponse(0);
1503 if (response instanceof ConnectionClosedResponse)
1504 {
1505 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
1506 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
1507 poolStatistics.incrementNumConnectionsClosedDefunct();
1508 conn = handleDefunctConnection(conn);
1509 if (conn != null)
1510 {
1511 examinedConnections.add(conn);
1512 }
1513 continue;
1514 }
1515 else if (response instanceof ExtendedResult)
1516 {
1517 // This means we got an unsolicited response. It could be a
1518 // notice of disconnection, or it could be something else, but in
1519 // any case we'll send it to the connection's unsolicited
1520 // notification handler (if one is defined).
1521 final UnsolicitedNotificationHandler h = conn.
1522 getConnectionOptions().getUnsolicitedNotificationHandler();
1523 if (h != null)
1524 {
1525 h.handleUnsolicitedNotification(conn,
1526 (ExtendedResult) response);
1527 }
1528 }
1529 else if (response instanceof LDAPResult)
1530 {
1531 final LDAPResult r = (LDAPResult) response;
1532 if (r.getResultCode() == ResultCode.SERVER_DOWN)
1533 {
1534 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
1535 ERR_POOL_HEALTH_CHECK_CONN_CLOSED.get(), null);
1536 poolStatistics.incrementNumConnectionsClosedDefunct();
1537 conn = handleDefunctConnection(conn);
1538 if (conn != null)
1539 {
1540 examinedConnections.add(conn);
1541 }
1542 continue;
1543 }
1544 }
1545 }
1546 catch (final LDAPException le)
1547 {
1548 if (le.getResultCode() == ResultCode.TIMEOUT)
1549 {
1550 debugException(Level.FINEST, le);
1551 }
1552 else
1553 {
1554 debugException(le);
1555 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
1556 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(
1557 getExceptionMessage(le)), le);
1558 poolStatistics.incrementNumConnectionsClosedDefunct();
1559 conn = handleDefunctConnection(conn);
1560 if (conn != null)
1561 {
1562 examinedConnections.add(conn);
1563 }
1564 continue;
1565 }
1566 }
1567 catch (final Exception e)
1568 {
1569 debugException(e);
1570 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
1571 ERR_POOL_HEALTH_CHECK_READ_FAILURE.get(getExceptionMessage(e)),
1572 e);
1573 poolStatistics.incrementNumConnectionsClosedDefunct();
1574 conn = handleDefunctConnection(conn);
1575 if (conn != null)
1576 {
1577 examinedConnections.add(conn);
1578 }
1579 continue;
1580 }
1581 finally
1582 {
1583 if (previousTimeout != Integer.MIN_VALUE)
1584 {
1585 try
1586 {
1587 s.setSoTimeout(previousTimeout);
1588 }
1589 catch (final Exception e)
1590 {
1591 debugException(e);
1592 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT,
1593 null, e);
1594 poolStatistics.incrementNumConnectionsClosedDefunct();
1595 conn = handleDefunctConnection(conn);
1596 if (conn != null)
1597 {
1598 examinedConnections.add(conn);
1599 }
1600 continue;
1601 }
1602 }
1603 }
1604 }
1605
1606 try
1607 {
1608 healthCheck.ensureConnectionValidForContinuedUse(conn);
1609 if (availableConnections.offer(conn))
1610 {
1611 examinedConnections.add(conn);
1612 }
1613 else
1614 {
1615 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_UNNEEDED,
1616 null, null);
1617 poolStatistics.incrementNumConnectionsClosedUnneeded();
1618 conn.terminate(null);
1619 }
1620 }
1621 catch (Exception e)
1622 {
1623 debugException(e);
1624 conn = handleDefunctConnection(conn);
1625 if (conn != null)
1626 {
1627 examinedConnections.add(conn);
1628 }
1629 }
1630 }
1631 }
1632 }
1633
1634
1635
1636 /**
1637 * {@inheritDoc}
1638 */
1639 @Override()
1640 public int getCurrentAvailableConnections()
1641 {
1642 return availableConnections.size();
1643 }
1644
1645
1646
1647 /**
1648 * {@inheritDoc}
1649 */
1650 @Override()
1651 public int getMaximumAvailableConnections()
1652 {
1653 return numConnections;
1654 }
1655
1656
1657
1658 /**
1659 * {@inheritDoc}
1660 */
1661 @Override()
1662 public LDAPConnectionPoolStatistics getConnectionPoolStatistics()
1663 {
1664 return poolStatistics;
1665 }
1666
1667
1668
1669 /**
1670 * Closes this connection pool in the event that it becomes unreferenced.
1671 *
1672 * @throws Throwable If an unexpected problem occurs.
1673 */
1674 @Override()
1675 protected void finalize()
1676 throws Throwable
1677 {
1678 super.finalize();
1679
1680 close();
1681 }
1682
1683
1684
1685 /**
1686 * {@inheritDoc}
1687 */
1688 @Override()
1689 public void toString(final StringBuilder buffer)
1690 {
1691 buffer.append("LDAPConnectionPool(");
1692
1693 final String name = connectionPoolName;
1694 if (name != null)
1695 {
1696 buffer.append("name='");
1697 buffer.append(name);
1698 buffer.append("', ");
1699 }
1700
1701 buffer.append("serverSet=");
1702 serverSet.toString(buffer);
1703 buffer.append(", maxConnections=");
1704 buffer.append(numConnections);
1705 buffer.append(')');
1706 }
1707 }