001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * docs/licenses/cddl.txt 011 * or http://www.opensource.org/licenses/cddl1.php. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * docs/licenses/cddl.txt. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2010-2014 UnboundID Corp. 026 */ 027 package com.unboundid.directory.sdk.proxy.api; 028 029 030 031 import java.util.Collections; 032 import java.util.List; 033 import java.util.Map; 034 035 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 036 import com.unboundid.directory.sdk.common.internal.Reconfigurable; 037 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 038 import com.unboundid.directory.sdk.proxy.config.LDAPHealthCheckConfig; 039 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 040 import com.unboundid.directory.sdk.proxy.types.BackendServer; 041 import com.unboundid.directory.sdk.proxy.types.CompletedProxyOperationContext; 042 import com.unboundid.directory.sdk.proxy.types.HealthCheckResult; 043 import com.unboundid.directory.sdk.proxy.types.ProxyServerContext; 044 import com.unboundid.ldap.sdk.LDAPConnection; 045 import com.unboundid.ldap.sdk.LDAPException; 046 import com.unboundid.ldap.sdk.ResultCode; 047 import com.unboundid.util.Extensible; 048 import com.unboundid.util.ThreadSafety; 049 import com.unboundid.util.ThreadSafetyLevel; 050 import com.unboundid.util.args.ArgumentException; 051 import com.unboundid.util.args.ArgumentParser; 052 053 054 055 /** 056 * This class defines an API that must be implemented by extensions which are 057 * used to assess the health of backend servers accessed through the Directory 058 * Proxy Server. Each health check invocation generates a health check result, 059 * which contains the following elements: 060 * <UL> 061 * <LI>Health Check State -- This indicates the general health state for the 062 * backend server. The state may be AVAILABLE (the server is completely 063 * suitable for use), DEGRADED (the server should be avoided if there are 064 * better servers but may be used if necessary), or UNAVAILABLE (the 065 * server not not be used at all).</LI> 066 * <LI>Health Check Score -- This provides an integer value between 10 (the 067 * best score) and 1 (the worst score) that may help rank servers with the 068 * same state. Some load-balancing algorithms (e.g., the health-weighted 069 * algorithm) may use this to prefer servers with a higher score over 070 * those with a lower score. Servers with a state of UNAVAILABLE will 071 * always have a score of zero.</LI> 072 * <LI>Messages -- This may optionally provide information about the reason 073 * for the state and/or score. This is primarily useful for results 074 * indicating that the server is DEGRADED or UNAVAILABLE to provide 075 * information about the problem(s) preventing it from being considered 076 * AVAILABLE.</LI> 077 * </UL> 078 * <BR><BR> 079 * LDAP health checks may be invoked in two ways. They will be invoked on a 080 * regular basis to periodically re-assess the health of each backend server, 081 * but they may also be invoked after a failed operation in order to more 082 * quickly detect a problem that should cause the server to be transitioned to a 083 * less-available state. The server will ensure that no more than one health 084 * check is in progress at a time for a given server in order to avoid 085 * overwhelming it with health checking, but it is still recommended that 086 * health checks triggered as a result of a failed operation attempt to use the 087 * operation result code to decide whether it may be necessary to actually 088 * attempt to communicate with the server. 089 * <BR><BR> 090 * Further, it may also be useful to have more stringent criteria for promoting 091 * the health of a server than for demoting it in order to avoid a ping-pong 092 * effect that may occur if a server is hovering near the border between 093 * AVAILABLE and DEGRADED or between DEGRADED and UNAVAILABLE. For example, if 094 * a health check attempts to perform an operation in the backend server and the 095 * response time for that operation is taken into account when determining the 096 * server health, it might be better to require a lower response time in order 097 * to transition from DEGRADED to AVAILABLE than was originally required to 098 * transition from AVAILABLE to DEGRADED. 099 * <BR> 100 * <H2>Configuring LDAP Health Checks</H2> 101 * In order to configure an LDAP health check created using this API, use a 102 * command like: 103 * <PRE> 104 * dsconfig create-ldap-health-check \ 105 * --check-name "<I>{check-name}</I>" \ 106 * --type third-party \ 107 * --set enabled:true \ 108 * --set "extension-class:<I>{class-name}</I>" \ 109 * --set "extension-argument:<I>{name=value}</I>" 110 * </PRE> 111 * where "<I>{check-name}</I>" is the name to use for the LDAP health check 112 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class 113 * that extends {@code com.unboundid.directory.sdk.proxy.api.LDAPHealthCheck}, 114 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to 115 * provide to the LDAP health check. If multiple arguments should be provided 116 * to the LDAP health check, then the 117 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 118 * provided multiple times. 119 * 120 * @see com.unboundid.directory.sdk.proxy.scripting.ScriptedLDAPHealthCheck 121 */ 122 @Extensible() 123 @DirectoryProxyServerExtension(appliesToLocalContent=false, 124 appliesToRemoteContent=true) 125 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 126 public abstract class LDAPHealthCheck 127 implements UnboundIDExtension, Reconfigurable<LDAPHealthCheckConfig>, 128 ExampleUsageProvider 129 { 130 /** 131 * Creates a new instance of this LDAP health check. All LDAP health check 132 * implementations must include a default constructor, but any initialization 133 * should generally be done in the {@code initializeLDAPHealthCheck} method. 134 */ 135 public LDAPHealthCheck() 136 { 137 // No implementation is required. 138 } 139 140 141 142 /** 143 * {@inheritDoc} 144 */ 145 public abstract String getExtensionName(); 146 147 148 149 /** 150 * {@inheritDoc} 151 */ 152 public abstract String[] getExtensionDescription(); 153 154 155 156 /** 157 * {@inheritDoc} 158 */ 159 public void defineConfigArguments(final ArgumentParser parser) 160 throws ArgumentException 161 { 162 // No arguments will be allowed by default. 163 } 164 165 166 167 /** 168 * Initializes this LDAP health check. 169 * 170 * @param serverContext A handle to the server context for the Directory 171 * Proxy server in which this extension is running. 172 * @param config The general configuration for this LDAP health 173 * check. 174 * @param parser The argument parser which has been initialized from 175 * the configuration for this LDAP health check. 176 * 177 * @throws LDAPException If a problem occurs while initializing this LDAP 178 * health check. 179 */ 180 public void initializeLDAPHealthCheck(final ProxyServerContext serverContext, 181 final LDAPHealthCheckConfig config, 182 final ArgumentParser parser) 183 throws LDAPException 184 { 185 // No initialization will be performed by default. 186 } 187 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 public boolean isConfigurationAcceptable(final LDAPHealthCheckConfig config, 194 final ArgumentParser parser, 195 final List<String> unacceptableReasons) 196 { 197 // No extended validation will be performed by default. 198 return true; 199 } 200 201 202 203 /** 204 * {@inheritDoc} 205 */ 206 public ResultCode applyConfiguration(final LDAPHealthCheckConfig config, 207 final ArgumentParser parser, 208 final List<String> adminActionsRequired, 209 final List<String> messages) 210 { 211 // By default, no configuration changes will be applied. If there are any 212 // arguments, then add an admin action message indicating that the extension 213 // needs to be restarted for any changes to take effect. 214 if (! parser.getNamedArguments().isEmpty()) 215 { 216 adminActionsRequired.add( 217 "No configuration change has actually been applied. The new " + 218 "configuration will not take effect until this LDAP health " + 219 "check is disabled and re-enabled or until the server is " + 220 "restarted."); 221 } 222 223 return ResultCode.SUCCESS; 224 } 225 226 227 228 /** 229 * Performs any cleanup which may be necessary when this LDAP health check is 230 * to be taken out of service. 231 */ 232 public void finalizeLDAPHealthCheck() 233 { 234 // No implementation is required. 235 } 236 237 238 239 /** 240 * Attempts to determine the health of the provided LDAP external server whose 241 * last health check result indicated that the server had a state of 242 * AVAILABLE. This method may be periodically invoked for AVAILABLE servers 243 * to determine whether their state has changed. 244 * 245 * @param backendServer A handle to the LDAP external server whose health is 246 * to be assessed. 247 * @param connection A connection that may be used to communicate with 248 * the server in the course of performing the 249 * evaluation. The health check should not do anything 250 * which may alter the state of this connection. 251 * 252 * @return Information about the result of the health check. 253 */ 254 public abstract HealthCheckResult checkAvailableServer( 255 final BackendServer backendServer, 256 final LDAPConnection connection); 257 258 259 260 /** 261 * Attempts to determine the health of the provided LDAP external server whose 262 * last health check result indicated that the server had a state of DEGRADED. 263 * This method may be periodically invoked for DEGRADED servers to determine 264 * whether their state has changed. 265 * 266 * @param backendServer A handle to the LDAP external server whose health is 267 * to be assessed. 268 * @param connection A connection that may be used to communicate with 269 * the server in the course of performing the 270 * evaluation. The health check should not do anything 271 * which may alter the state of this connection. 272 * 273 * @return Information about the result of the health check. 274 */ 275 public abstract HealthCheckResult checkDegradedServer( 276 final BackendServer backendServer, 277 final LDAPConnection connection); 278 279 280 281 /** 282 * Attempts to determine the health of the provided LDAP external server whose 283 * last health check result indicated that the server had a state of 284 * UNAVAILABLE. This method may be periodically invoked for UNAVAILABLE 285 * servers to determine whether their state has changed. 286 * 287 * @param backendServer A handle to the LDAP external server whose health is 288 * to be assessed. 289 * @param connection A connection that may be used to communicate with 290 * the server in the course of performing the 291 * evaluation. The health check should not do anything 292 * which may alter the state of this connection. 293 * 294 * @return Information about the result of the health check. 295 */ 296 public abstract HealthCheckResult checkUnavailableServer( 297 final BackendServer backendServer, 298 final LDAPConnection connection); 299 300 301 302 /** 303 * Attempts to determine the health of the provided LDAP external server in 304 * which an attempted operation did not complete successfully. 305 * 306 * @param operationContext A handle to the operation context for the 307 * operation that failed. 308 * @param exception The exception caught when attempting to process 309 * the associated operation in the backend server. 310 * @param backendServer A handle to the backend server in which the 311 * operation was processed. 312 * 313 * @return Information about the result of the health check. 314 */ 315 public abstract HealthCheckResult checkFailedOperation( 316 final CompletedProxyOperationContext operationContext, 317 final LDAPException exception, 318 final BackendServer backendServer); 319 320 321 322 /** 323 * Retrieves a string representation of this LDAP health check. 324 * 325 * @return A string representation of this LDAP health check. 326 */ 327 @Override() 328 public final String toString() 329 { 330 final StringBuilder buffer = new StringBuilder(); 331 toString(buffer); 332 return buffer.toString(); 333 } 334 335 336 337 /** 338 * Appends a string representation of this LDAP health check to the provided 339 * buffer. 340 * 341 * @param buffer The buffer to which the string representation should be 342 * appended. 343 */ 344 public abstract void toString(final StringBuilder buffer); 345 346 347 348 /** 349 * {@inheritDoc} 350 */ 351 public Map<List<String>,String> getExamplesArgumentSets() 352 { 353 return Collections.emptyMap(); 354 } 355 }