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