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 * Portions Copyright 2010-2023 Ping Identity Corporation 026 */ 027package com.unboundid.directory.sdk.proxy.scripting; 028 029 030 031import java.util.List; 032 033import com.unboundid.directory.sdk.common.internal.Reconfigurable; 034import com.unboundid.directory.sdk.proxy.config.LDAPHealthCheckConfig; 035import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 036import com.unboundid.directory.sdk.proxy.types.BackendServer; 037import com.unboundid.directory.sdk.proxy.types.CompletedProxyOperationContext; 038import com.unboundid.directory.sdk.proxy.types.HealthCheckResult; 039import com.unboundid.directory.sdk.proxy.types.ProxyServerContext; 040import com.unboundid.ldap.sdk.LDAPConnection; 041import com.unboundid.ldap.sdk.LDAPException; 042import com.unboundid.ldap.sdk.ResultCode; 043import com.unboundid.util.Extensible; 044import com.unboundid.util.ThreadSafety; 045import com.unboundid.util.ThreadSafetyLevel; 046import com.unboundid.util.args.ArgumentException; 047import com.unboundid.util.args.ArgumentParser; 048 049 050 051/** 052 * This class defines an API that must be implemented by scripted extensions 053 * which are used to assess the health of backend servers accessed through the 054 * Directory Proxy Server. Each health check invocation generates a health 055 * check result, which contains the following elements: 056 * <UL> 057 * <LI>Health Check State -- This indicates the general health state for the 058 * backend server. The state may be AVAILABLE (the server is completely 059 * suitable for use), DEGRADED (the server should be avoided if there are 060 * better servers but may be used if necessary), or UNAVAILABLE (the 061 * server should not be used at all).</LI> 062 * <LI>Health Check Score -- This provides an integer value between 10 (the 063 * best score) and 1 (the worst score) that may help rank servers with the 064 * same state. Some load-balancing algorithms (e.g., the health-weighted 065 * algorithm) may use this to prefer servers with a higher score over 066 * those with a lower score. Servers with a state of UNAVAILABLE will 067 * always have a score of zero.</LI> 068 * <LI>Messages -- This may optionally provide information about the reason 069 * for the state and/or score. This is primarily useful for results 070 * indicating that the server is DEGRADED or UNAVAILABLE to provide 071 * information about the problem(s) preventing it from being considered 072 * AVAILABLE.</LI> 073 * </UL> 074 * <BR><BR> 075 * LDAP health checks may be invoked in two ways. They will be invoked on a 076 * regular basis to periodically re-assess the health of each backend server, 077 * but they may also be invoked after a failed operation in order to more 078 * quickly detect a problem that should cause the server to be transitioned to a 079 * less-available state. The server will ensure that no more than one health 080 * check is in progress at a time for a given server in order to avoid 081 * overwhelming it with health checking, but it is still recommended that 082 * health checks triggered as a result of a failed operation attempt to use the 083 * operation result code to decide whether it may be necessary to actually 084 * attempt to communicate with the server. 085 * <BR><BR> 086 * Further, it may also be useful to have more stringent criteria for promoting 087 * the health of a server than for demoting it in order to avoid a ping-pong 088 * effect that may occur if a server is hovering near the border between 089 * AVAILABLE and DEGRADED or between DEGRADED and UNAVAILABLE. For example, if 090 * a health check attempts to perform an operation in the backend server and the 091 * response time for that operation is taken into account when determining the 092 * server health, it might be better to require a lower response time in order 093 * to transition from DEGRADED to AVAILABLE than was originally required to 094 * transition from AVAILABLE to DEGRADED. 095 * <BR> 096 * <H2>Configuring Groovy-Scripted LDAP Health Checks</H2> 097 * In order to configure a scripted LDAP health check based on this API and 098 * written in the Groovy scripting language, use a command like: 099 * <PRE> 100 * dsconfig create-ldap-health-check \ 101 * --check-name "<I>{check-name}</I>" \ 102 * --type groovy-scripted \ 103 * --set enabled:true \ 104 * --set "script-class:<I>{class-name}</I>" \ 105 * --set "script-argument:<I>{name=value}</I>" 106 * </PRE> 107 * where "<I>{check-name}</I>" is the name to use for the LDAP health check 108 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Groovy 109 * class written using this API, and "<I>{name=value}</I>" represents name-value 110 * pairs for any arguments to provide to the LDAP health check. If multiple 111 * arguments should be provided to the LDAP health check, then the 112 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be 113 * provided multiple times. 114 * 115 * @see com.unboundid.directory.sdk.proxy.api.LDAPHealthCheck 116 */ 117@Extensible() 118@DirectoryProxyServerExtension(appliesToLocalContent=false, 119 appliesToRemoteContent=true) 120@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 121public abstract class ScriptedLDAPHealthCheck 122 implements Reconfigurable<LDAPHealthCheckConfig> 123{ 124 /** 125 * Creates a new instance of this LDAP health check. All LDAP health check 126 * implementations must include a default constructor, but any initialization 127 * should generally be done in the {@code initializeLDAPHealthCheck} method. 128 */ 129 public ScriptedLDAPHealthCheck() 130 { 131 // No implementation is required. 132 } 133 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 public void defineConfigArguments(final ArgumentParser parser) 140 throws ArgumentException 141 { 142 // No arguments will be allowed by default. 143 } 144 145 146 147 /** 148 * Initializes this LDAP health check. 149 * 150 * @param serverContext A handle to the server context for the server in 151 * which this extension is running. 152 * @param config The general configuration for this LDAP health 153 * check. 154 * @param parser The argument parser which has been initialized from 155 * the configuration for this LDAP health check. 156 * 157 * @throws LDAPException If a problem occurs while initializing this LDAP 158 * health check. 159 */ 160 public void initializeLDAPHealthCheck( 161 final ProxyServerContext serverContext, 162 final LDAPHealthCheckConfig config, 163 final ArgumentParser parser) 164 throws LDAPException 165 { 166 // No initialization will be performed by default. 167 } 168 169 170 171 /** 172 * Performs any cleanup which may be necessary when this LDAP health check is 173 * to be taken out of service. 174 */ 175 public void finalizeLDAPHealthCheck() 176 { 177 // No implementation is required. 178 } 179 180 181 182 /** 183 * {@inheritDoc} 184 */ 185 public boolean isConfigurationAcceptable(final LDAPHealthCheckConfig config, 186 final ArgumentParser parser, 187 final List<String> unacceptableReasons) 188 { 189 // No extended validation will be performed. 190 return true; 191 } 192 193 194 195 /** 196 * {@inheritDoc} 197 */ 198 public ResultCode applyConfiguration(final LDAPHealthCheckConfig config, 199 final ArgumentParser parser, 200 final List<String> adminActionsRequired, 201 final List<String> messages) 202 { 203 // By default, no configuration changes will be applied. If there are any 204 // arguments, then add an admin action message indicating that the extension 205 // needs to be restarted for any changes to take effect. 206 if (! parser.getNamedArguments().isEmpty()) 207 { 208 adminActionsRequired.add( 209 "No configuration change has actually been applied. The new " + 210 "configuration will not take effect until this LDAP health " + 211 "check is disabled and re-enabled or until the server is " + 212 "restarted."); 213 } 214 215 return ResultCode.SUCCESS; 216 } 217 218 219 220 /** 221 * Attempts to determine the health of the provided LDAP external server whose 222 * last health check result indicated that the server had a state of 223 * AVAILABLE. This method may be periodically invoked for AVAILABLE servers 224 * to determine whether their state has changed. 225 * 226 * @param backendServer A handle to the LDAP external server whose health is 227 * to be assessed. 228 * @param connection A connection that may be used to communicate with 229 * the server in the course of performing the 230 * evaluation. The health check should not do anything 231 * which may alter the state of this connection. 232 * 233 * @return Information about the result of the health check. 234 */ 235 public abstract HealthCheckResult checkAvailableServer( 236 final BackendServer backendServer, 237 final LDAPConnection connection); 238 239 240 241 /** 242 * Attempts to determine the health of the provided LDAP external server whose 243 * last health check result indicated that the server had a state of DEGRADED. 244 * This method may be periodically invoked for DEGRADED servers to determine 245 * whether their state has changed. 246 * 247 * @param backendServer A handle to the LDAP external server whose health is 248 * to be assessed. 249 * @param connection A connection that may be used to communicate with 250 * the server in the course of performing the 251 * evaluation. The health check should not do anything 252 * which may alter the state of this connection. 253 * 254 * @return Information about the result of the health check. 255 */ 256 public abstract HealthCheckResult checkDegradedServer( 257 final BackendServer backendServer, 258 final LDAPConnection connection); 259 260 261 262 /** 263 * Attempts to determine the health of the provided LDAP external server whose 264 * last health check result indicated that the server had a state of 265 * UNAVAILABLE. This method may be periodically invoked for UNAVAILABLE 266 * servers to determine whether their state has changed. 267 * 268 * @param backendServer A handle to the LDAP external server whose health is 269 * to be assessed. 270 * @param connection A connection that may be used to communicate with 271 * the server in the course of performing the 272 * evaluation. The health check should not do anything 273 * which may alter the state of this connection. 274 * 275 * @return Information about the result of the health check. 276 */ 277 public abstract HealthCheckResult checkUnavailableServer( 278 final BackendServer backendServer, 279 final LDAPConnection connection); 280 281 282 283 /** 284 * Attempts to determine the health of the provided LDAP external server in 285 * which an attempted operation did not complete successfully. 286 * 287 * @param operationContext A handle to the operation context for the 288 * operation that failed. 289 * @param exception The exception caught when attempting to process 290 * the associated operation in the backend server. 291 * @param backendServer A handle to the backend server in which the 292 * operation was processed. 293 * 294 * @return Information about the result of the health check. 295 */ 296 public abstract HealthCheckResult checkFailedOperation( 297 final CompletedProxyOperationContext operationContext, 298 final LDAPException exception, 299 final BackendServer backendServer); 300}