001/* 002 * Copyright 2009-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2017 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.io.Serializable; 026 027import com.unboundid.util.NotMutable; 028import com.unboundid.util.ThreadSafety; 029import com.unboundid.util.ThreadSafetyLevel; 030 031import static com.unboundid.ldap.sdk.LDAPMessages.*; 032import static com.unboundid.util.Debug.*; 033import static com.unboundid.util.StaticUtils.*; 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 = 30000L; 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 during background health checks. 073 private final boolean invokeForBackgroundChecks; 074 075 // Indicates whether to invoke the test when checking out a connection. 076 private final boolean invokeOnCheckout; 077 078 // Indicates whether to invoke the test when creating a new connection. 079 private final boolean invokeOnCreate; 080 081 // Indicates whether to invoke the test whenever an exception is encountered 082 // when using the connection. 083 private final boolean invokeOnException; 084 085 // Indicates whether to invoke the test when releasing a connection. 086 private final boolean invokeOnRelease; 087 088 // The maximum response time value in milliseconds. 089 private final long maxResponseTime; 090 091 // The search request to send to the server. 092 private final SearchRequest searchRequest; 093 094 // The DN of the entry to retrieve. 095 private final String entryDN; 096 097 098 099 /** 100 * Creates a new instance of this get entry LDAP connection pool health check. 101 * 102 * @param entryDN The DN of the entry to retrieve from 103 * the target server. If this is 104 * {@code null}, then the server's root DSE 105 * will be used. 106 * @param maxResponseTime The maximum length of time in 107 * milliseconds that should be allowed when 108 * attempting to retrieve the entry. If 109 * the provided value is less than or equal 110 * to zero, then the default value of 30000 111 * milliseconds (30 seconds) will be used. 112 * @param invokeOnCreate Indicates whether to test for the 113 * existence of the target entry whenever a 114 * new connection is created for use in the 115 * pool. 116 * @param invokeOnCheckout Indicates whether to test for the 117 * existence of the target entry 118 * immediately before a connection is 119 * checked out of the pool. 120 * @param invokeOnRelease Indicates whether to test for the 121 * existence of the target entry 122 * immediately after a connection has been 123 * released back to the pool. 124 * @param invokeForBackgroundChecks Indicates whether to test for the 125 * existence of the target entry during 126 * periodic background health checks. 127 * @param invokeOnException Indicates whether to test for the 128 * existence of the target entry if an 129 * exception is encountered when using the 130 * connection. 131 */ 132 public GetEntryLDAPConnectionPoolHealthCheck(final String entryDN, 133 final long maxResponseTime, final boolean invokeOnCreate, 134 final boolean invokeOnCheckout, final boolean invokeOnRelease, 135 final boolean invokeForBackgroundChecks, 136 final boolean invokeOnException) 137 { 138 this.invokeOnCreate = invokeOnCreate; 139 this.invokeOnCheckout = invokeOnCheckout; 140 this.invokeOnRelease = invokeOnRelease; 141 this.invokeForBackgroundChecks = invokeForBackgroundChecks; 142 this.invokeOnException = invokeOnException; 143 144 if (entryDN == null) 145 { 146 this.entryDN = ""; 147 } 148 else 149 { 150 this.entryDN = entryDN; 151 } 152 153 if (maxResponseTime > 0L) 154 { 155 this.maxResponseTime = maxResponseTime; 156 } 157 else 158 { 159 this.maxResponseTime = DEFAULT_MAX_RESPONSE_TIME; 160 } 161 162 searchRequest = new SearchRequest(this.entryDN, SearchScope.BASE, 163 Filter.createPresenceFilter("objectClass"), "1.1"); 164 searchRequest.setResponseTimeoutMillis(this.maxResponseTime); 165 } 166 167 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override() 173 public void ensureNewConnectionValid(final LDAPConnection connection) 174 throws LDAPException 175 { 176 if (invokeOnCreate) 177 { 178 getEntry(connection); 179 } 180 } 181 182 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override() 188 public void ensureConnectionValidForCheckout(final LDAPConnection connection) 189 throws LDAPException 190 { 191 if (invokeOnCheckout) 192 { 193 getEntry(connection); 194 } 195 } 196 197 198 199 /** 200 * {@inheritDoc} 201 */ 202 @Override() 203 public void ensureConnectionValidForRelease(final LDAPConnection connection) 204 throws LDAPException 205 { 206 if (invokeOnRelease) 207 { 208 getEntry(connection); 209 } 210 } 211 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override() 218 public void ensureConnectionValidForContinuedUse( 219 final LDAPConnection connection) 220 throws LDAPException 221 { 222 if (invokeForBackgroundChecks) 223 { 224 getEntry(connection); 225 } 226 } 227 228 229 230 /** 231 * {@inheritDoc} 232 */ 233 @Override() 234 public void ensureConnectionValidAfterException( 235 final LDAPConnection connection, 236 final LDAPException exception) 237 throws LDAPException 238 { 239 super.ensureConnectionValidAfterException(connection, exception); 240 241 if (invokeOnException) 242 { 243 getEntry(connection); 244 } 245 } 246 247 248 249 /** 250 * Retrieves the DN of the entry that will be retrieved when performing the 251 * health checks. 252 * 253 * @return The DN of the entry that will be retrieved when performing the 254 * health checks. 255 */ 256 public String getEntryDN() 257 { 258 return entryDN; 259 } 260 261 262 263 /** 264 * Retrieves the maximum length of time in milliseconds that this health 265 * check should wait for the entry to be returned. 266 * 267 * @return The maximum length of time in milliseconds that this health check 268 * should wait for the entry to be returned. 269 */ 270 public long getMaxResponseTimeMillis() 271 { 272 return maxResponseTime; 273 } 274 275 276 277 /** 278 * Indicates whether this health check will test for the existence of the 279 * target entry whenever a new connection is created. 280 * 281 * @return {@code true} if this health check will test for the existence of 282 * the target entry whenever a new connection is created, or 283 * {@code false} if not. 284 */ 285 public boolean invokeOnCreate() 286 { 287 return invokeOnCreate; 288 } 289 290 291 292 /** 293 * Indicates whether this health check will test for the existence of the 294 * target entry whenever a connection is to be checked out for use. 295 * 296 * @return {@code true} if this health check will test for the existence of 297 * the target entry whenever a connection is to be checked out, or 298 * {@code false} if not. 299 */ 300 public boolean invokeOnCheckout() 301 { 302 return invokeOnCheckout; 303 } 304 305 306 307 /** 308 * Indicates whether this health check will test for the existence of the 309 * target entry whenever a connection is to be released back to the pool. 310 * 311 * @return {@code true} if this health check will test for the existence of 312 * the target entry whenever a connection is to be released, or 313 * {@code false} if not. 314 */ 315 public boolean invokeOnRelease() 316 { 317 return invokeOnRelease; 318 } 319 320 321 322 /** 323 * Indicates whether this health check will test for the existence of the 324 * target entry during periodic background health checks. 325 * 326 * @return {@code true} if this health check will test for the existence of 327 * the target entry during periodic background health checks, or 328 * {@code false} if not. 329 */ 330 public boolean invokeForBackgroundChecks() 331 { 332 return invokeForBackgroundChecks; 333 } 334 335 336 337 /** 338 * Indicates whether this health check will test for the existence of the 339 * target entry if an exception is caught while processing an operation on a 340 * connection. 341 * 342 * @return {@code true} if this health check will test for the existence of 343 * the target entry whenever an exception is caught, or {@code false} 344 * if not. 345 */ 346 public boolean invokeOnException() 347 { 348 return invokeOnException; 349 } 350 351 352 353 /** 354 * Attempts to retrieve the target entry. If the attempt fails, or if the 355 * connection takes too long then an exception will be thrown. 356 * 357 * @param conn The connection to be checked. 358 * 359 * @throws LDAPException If a problem occurs while trying to retrieve the 360 * entry, or if it cannot be retrieved in an 361 * acceptable length of time. 362 */ 363 private void getEntry(final LDAPConnection conn) 364 throws LDAPException 365 { 366 try 367 { 368 final SearchResult result = conn.search(searchRequest.duplicate()); 369 if (result.getEntryCount() != 1) 370 { 371 throw new LDAPException(ResultCode.NO_RESULTS_RETURNED, 372 ERR_GET_ENTRY_HEALTH_CHECK_NO_ENTRY_RETURNED.get()); 373 } 374 } 375 catch (Exception e) 376 { 377 debugException(e); 378 379 final String msg = ERR_GET_ENTRY_HEALTH_CHECK_FAILURE.get(entryDN, 380 getExceptionMessage(e)); 381 382 conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, msg, e); 383 throw new LDAPException(ResultCode.SERVER_DOWN, msg, e); 384 } 385 } 386 387 388 389 /** 390 * {@inheritDoc} 391 */ 392 @Override() 393 public void toString(final StringBuilder buffer) 394 { 395 buffer.append("GetEntryLDAPConnectionPoolHealthCheck(entryDN='"); 396 buffer.append(entryDN); 397 buffer.append("', maxResponseTimeMillis="); 398 buffer.append(maxResponseTime); 399 buffer.append(", invokeOnCreate="); 400 buffer.append(invokeOnCreate); 401 buffer.append(", invokeOnCheckout="); 402 buffer.append(invokeOnCheckout); 403 buffer.append(", invokeOnRelease="); 404 buffer.append(invokeOnRelease); 405 buffer.append(", invokeForBackgroundChecks="); 406 buffer.append(invokeForBackgroundChecks); 407 buffer.append(", invokeOnException="); 408 buffer.append(invokeOnException); 409 buffer.append(')'); 410 } 411}