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 }