001/* 002 * Copyright 2009-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.io.Serializable; 026 027import com.unboundid.util.Debug; 028import com.unboundid.util.NotMutable; 029import com.unboundid.util.StaticUtils; 030import com.unboundid.util.ThreadSafety; 031import com.unboundid.util.ThreadSafetyLevel; 032 033import static com.unboundid.ldap.sdk.LDAPMessages.*; 034 035 036 037/** 038 * This class provides an LDAP connection pool health check implementation that 039 * may be used to check the health of the associated server by verifying that a 040 * specified entry can be retrieved in an acceptable period of time. If the 041 * entry cannot be retrieved (either because it does not exist, or because an 042 * error occurs while attempting to retrieve it), or if it takes too long to 043 * retrieve the entry, then the associated connection will be classified as 044 * unavailable. 045 * <BR><BR> 046 * It is possible to control under which conditions an attempt should be made to 047 * retrieve the target entry, and also to specify a maximum acceptable response 048 * time. For best results, the target entry should be available to be retrieved 049 * by a client with any authentication state. 050 */ 051@NotMutable() 052@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 053public final class GetEntryLDAPConnectionPoolHealthCheck 054 extends LDAPConnectionPoolHealthCheck 055 implements Serializable 056{ 057 /** 058 * The default maximum response time value in milliseconds, which is set to 059 * 30,000 milliseconds or 30 seconds. 060 */ 061 private static final long DEFAULT_MAX_RESPONSE_TIME = 30_000L; 062 063 064 065 /** 066 * The serial version UID for this serializable class. 067 */ 068 private static final long serialVersionUID = -3400259782503254645L; 069 070 071 072 // Indicates whether to invoke the test after a connection has been 073 // authenticated. 074 private final boolean invokeAfterAuthentication; 075 076 // Indicates whether to invoke the test during background health checks. 077 private final boolean invokeForBackgroundChecks; 078 079 // Indicates whether to invoke the test when checking out a connection. 080 private final boolean invokeOnCheckout; 081 082 // Indicates whether to invoke the test when creating a new connection. 083 private final boolean invokeOnCreate; 084 085 // Indicates whether to invoke the test whenever an exception is encountered 086 // when using the connection. 087 private final boolean invokeOnException; 088 089 // Indicates whether to invoke the test when releasing a connection. 090 private final boolean invokeOnRelease; 091 092 // The maximum response time value in milliseconds. 093 private final long maxResponseTime; 094 095 // The search request to send to the server. 096 private final SearchRequest searchRequest; 097 098 // The DN of the entry to retrieve. 099 private final String entryDN; 100 101 102 103 /** 104 * Creates a new instance of this get entry LDAP connection pool health check. 105 * 106 * @param entryDN The DN of the entry to retrieve from 107 * the target server. If this is 108 * {@code null}, then the server's root DSE 109 * will be used. 110 * @param maxResponseTime The maximum length of time in 111 * milliseconds that should be allowed when 112 * attempting to retrieve the entry. If 113 * the provided value is less than or equal 114 * to zero, then the default value of 30000 115 * milliseconds (30 seconds) will be used. 116 * @param invokeOnCreate Indicates whether to test for the 117 * existence of the target entry whenever a 118 * new connection is created for use in the 119 * pool. Note that this check will be 120 * performed immediately after the 121 * connection has been established and 122 * before any attempt has been made to 123 * authenticate that connection. 124 * @param invokeOnCheckout Indicates whether to test for the 125 * existence of the target entry 126 * immediately before a connection is 127 * checked out of the pool. 128 * @param invokeOnRelease Indicates whether to test for the 129 * existence of the target entry 130 * immediately after a connection has been 131 * released back to the pool. 132 * @param invokeForBackgroundChecks Indicates whether to test for the 133 * existence of the target entry during 134 * periodic background health checks. 135 * @param invokeOnException Indicates whether to test for the 136 * existence of the target entry if an 137 * exception is encountered when using the 138 * connection. 139 */ 140 public GetEntryLDAPConnectionPoolHealthCheck(final String entryDN, 141 final long maxResponseTime, final boolean invokeOnCreate, 142 final boolean invokeOnCheckout, final boolean invokeOnRelease, 143 final boolean invokeForBackgroundChecks, 144 final boolean invokeOnException) 145 { 146 this(entryDN, maxResponseTime, invokeOnCreate, false, invokeOnCheckout, 147 invokeOnRelease, invokeForBackgroundChecks, invokeOnException); 148 } 149 150 151 152 /** 153 * Creates a new instance of this get entry LDAP connection pool health check. 154 * 155 * @param entryDN 156 * The DN of the entry to retrieve from the target server. If 157 * this is {@code null}, then the server's root DSE will be used. 158 * @param maxResponseTime 159 * The maximum length of time in milliseconds that should be 160 * allowed when attempting to retrieve the entry. If the 161 * provided value is less than or equal to zero, then the 162 * default value of 30000 milliseconds (30 seconds) will be used. 163 * @param invokeOnCreate 164 * Indicates whether to test for the existence of the target 165 * entry whenever a new connection is created for use in the 166 * pool. Note that this check will be performed immediately 167 * after the connection has been established and before any 168 * attempt has been made to authenticate that connection. 169 * @param invokeAfterAuthentication 170 * Indicates whether to test for the existence of the target 171 * entry immediately after a connection has been authenticated. 172 * This includes immediately after a newly-created connection 173 * has been authenticated, after a call to the connection pool's 174 * {@code bindAndRevertAuthentication} method, and after a call 175 * to the connection pool's 176 * {@code releaseAndReAuthenticateConnection} method. Note that 177 * even if this is {@code true}, the health check will only be 178 * performed if the provided bind result indicates that the bind 179 * was successful. 180 * @param invokeOnCheckout 181 * Indicates whether to test for the existence of the target 182 * entry immediately before a connection is checked out of the 183 * pool. 184 * @param invokeOnRelease 185 * Indicates whether to test for the existence of the target 186 * entry immediately after a connection has been released back 187 * to the pool. 188 * @param invokeForBackgroundChecks 189 * Indicates whether to test for the existence of the target 190 * entry during periodic background health checks. 191 * @param invokeOnException 192 * Indicates whether to test for the existence of the target 193 * entry if an exception is encountered when using the 194 * connection. 195 */ 196 public GetEntryLDAPConnectionPoolHealthCheck(final String entryDN, 197 final long maxResponseTime, final boolean invokeOnCreate, 198 final boolean invokeAfterAuthentication, 199 final boolean invokeOnCheckout, final boolean invokeOnRelease, 200 final boolean invokeForBackgroundChecks, 201 final boolean invokeOnException) 202 { 203 this.invokeOnCreate = invokeOnCreate; 204 this.invokeAfterAuthentication = invokeAfterAuthentication; 205 this.invokeOnCheckout = invokeOnCheckout; 206 this.invokeOnRelease = invokeOnRelease; 207 this.invokeForBackgroundChecks = invokeForBackgroundChecks; 208 this.invokeOnException = invokeOnException; 209 210 if (entryDN == null) 211 { 212 this.entryDN = ""; 213 } 214 else 215 { 216 this.entryDN = entryDN; 217 } 218 219 if (maxResponseTime > 0L) 220 { 221 this.maxResponseTime = maxResponseTime; 222 } 223 else 224 { 225 this.maxResponseTime = DEFAULT_MAX_RESPONSE_TIME; 226 } 227 228 searchRequest = new SearchRequest(this.entryDN, SearchScope.BASE, 229 Filter.createPresenceFilter("objectClass"), "1.1"); 230 searchRequest.setResponseTimeoutMillis(this.maxResponseTime); 231 } 232 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override() 239 public void ensureNewConnectionValid(final LDAPConnection connection) 240 throws LDAPException 241 { 242 if (invokeOnCreate) 243 { 244 getEntry(connection); 245 } 246 } 247 248 249 250 /** 251 * {@inheritDoc} 252 */ 253 @Override() 254 public void ensureConnectionValidAfterAuthentication( 255 final LDAPConnection connection, final BindResult bindResult) 256 throws LDAPException 257 { 258 if (invokeAfterAuthentication && 259 (bindResult.getResultCode() == ResultCode.SUCCESS)) 260 { 261 getEntry(connection); 262 } 263 } 264 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override() 271 public void ensureConnectionValidForCheckout(final LDAPConnection connection) 272 throws LDAPException 273 { 274 if (invokeOnCheckout) 275 { 276 getEntry(connection); 277 } 278 } 279 280 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override() 286 public void ensureConnectionValidForRelease(final LDAPConnection connection) 287 throws LDAPException 288 { 289 if (invokeOnRelease) 290 { 291 getEntry(connection); 292 } 293 } 294 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override() 301 public void ensureConnectionValidForContinuedUse( 302 final LDAPConnection connection) 303 throws LDAPException 304 { 305 if (invokeForBackgroundChecks) 306 { 307 getEntry(connection); 308 } 309 } 310 311 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override() 317 public void ensureConnectionValidAfterException( 318 final LDAPConnection connection, 319 final LDAPException exception) 320 throws LDAPException 321 { 322 if (invokeOnException && 323 (! ResultCode.isConnectionUsable(exception.getResultCode()))) 324 { 325 getEntry(connection); 326 } 327 } 328 329 330 331 /** 332 * Retrieves the DN of the entry that will be retrieved when performing the 333 * health checks. 334 * 335 * @return The DN of the entry that will be retrieved when performing the 336 * health checks. 337 */ 338 public String getEntryDN() 339 { 340 return entryDN; 341 } 342 343 344 345 /** 346 * Retrieves the maximum length of time in milliseconds that this health 347 * check should wait for the entry to be returned. 348 * 349 * @return The maximum length of time in milliseconds that this health check 350 * should wait for the entry to be returned. 351 */ 352 public long getMaxResponseTimeMillis() 353 { 354 return maxResponseTime; 355 } 356 357 358 359 /** 360 * Indicates whether this health check will test for the existence of the 361 * target entry whenever a new connection is created. 362 * 363 * @return {@code true} if this health check will test for the existence of 364 * the target entry whenever a new connection is created, or 365 * {@code false} if not. 366 */ 367 public boolean invokeOnCreate() 368 { 369 return invokeOnCreate; 370 } 371 372 373 374 /** 375 * Indicates whether this health check will test for the existence of the 376 * target entry after a connection has been authenticated, including after 377 * authenticating a newly-created connection, as well as after calls to the 378 * connection pool's {@code bindAndRevertAuthentication} and 379 * {@code releaseAndReAuthenticateConnection} methods. 380 * 381 * @return {@code true} if this health check will test for the existence of 382 * the target entry whenever a connection has been authenticated, or 383 * {@code false} if not. 384 */ 385 public boolean invokeAfterAuthentication() 386 { 387 return invokeAfterAuthentication; 388 } 389 390 391 392 /** 393 * Indicates whether this health check will test for the existence of the 394 * target entry whenever a connection is to be checked out for use. 395 * 396 * @return {@code true} if this health check will test for the existence of 397 * the target entry whenever a connection is to be checked out, or 398 * {@code false} if not. 399 */ 400 public boolean invokeOnCheckout() 401 { 402 return invokeOnCheckout; 403 } 404 405 406 407 /** 408 * Indicates whether this health check will test for the existence of the 409 * target entry whenever a connection is to be released back to the pool. 410 * 411 * @return {@code true} if this health check will test for the existence of 412 * the target entry whenever a connection is to be released, or 413 * {@code false} if not. 414 */ 415 public boolean invokeOnRelease() 416 { 417 return invokeOnRelease; 418 } 419 420 421 422 /** 423 * Indicates whether this health check will test for the existence of the 424 * target entry during periodic background health checks. 425 * 426 * @return {@code true} if this health check will test for the existence of 427 * the target entry during periodic background health checks, or 428 * {@code false} if not. 429 */ 430 public boolean invokeForBackgroundChecks() 431 { 432 return invokeForBackgroundChecks; 433 } 434 435 436 437 /** 438 * Indicates whether this health check will test for the existence of the 439 * target entry if an exception is caught while processing an operation on a 440 * connection. 441 * 442 * @return {@code true} if this health check will test for the existence of 443 * the target entry whenever an exception is caught, or {@code false} 444 * if not. 445 */ 446 public boolean invokeOnException() 447 { 448 return invokeOnException; 449 } 450 451 452 453 /** 454 * Attempts to retrieve the target entry. If the attempt fails, or if the 455 * connection takes too long then an exception will be thrown. 456 * 457 * @param conn The connection to be checked. 458 * 459 * @throws LDAPException If a problem occurs while trying to retrieve the 460 * entry, or if it cannot be retrieved in an 461 * acceptable length of time. 462 */ 463 private void getEntry(final LDAPConnection conn) 464 throws LDAPException 465 { 466 try 467 { 468 final SearchResult result = conn.search(searchRequest.duplicate()); 469 if (result.getEntryCount() != 1) 470 { 471 throw new LDAPException(ResultCode.NO_RESULTS_RETURNED, 472 ERR_GET_ENTRY_HEALTH_CHECK_NO_ENTRY_RETURNED.get()); 473 } 474 } 475 catch (final Exception e) 476 { 477 Debug.debugException(e); 478 479 final String msg = ERR_GET_ENTRY_HEALTH_CHECK_FAILURE.get(entryDN, 480 StaticUtils.getExceptionMessage(e)); 481 482 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, msg, e); 483 throw new LDAPException(ResultCode.SERVER_DOWN, msg, e); 484 } 485 } 486 487 488 489 /** 490 * {@inheritDoc} 491 */ 492 @Override() 493 public void toString(final StringBuilder buffer) 494 { 495 buffer.append("GetEntryLDAPConnectionPoolHealthCheck(entryDN='"); 496 buffer.append(entryDN); 497 buffer.append("', maxResponseTimeMillis="); 498 buffer.append(maxResponseTime); 499 buffer.append(", invokeOnCreate="); 500 buffer.append(invokeOnCreate); 501 buffer.append(", invokeAfterAuthentication="); 502 buffer.append(invokeAfterAuthentication); 503 buffer.append(", invokeOnCheckout="); 504 buffer.append(invokeOnCheckout); 505 buffer.append(", invokeOnRelease="); 506 buffer.append(invokeOnRelease); 507 buffer.append(", invokeForBackgroundChecks="); 508 buffer.append(invokeForBackgroundChecks); 509 buffer.append(", invokeOnException="); 510 buffer.append(invokeOnException); 511 buffer.append(')'); 512 } 513}