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-2024 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.proxy.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.directory.sdk.proxy.config.LDAPHealthCheckConfig;
039import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
040import com.unboundid.directory.sdk.proxy.types.BackendServer;
041import com.unboundid.directory.sdk.proxy.types.CompletedProxyOperationContext;
042import com.unboundid.directory.sdk.proxy.types.HealthCheckResult;
043import com.unboundid.directory.sdk.proxy.types.ProxyServerContext;
044import com.unboundid.ldap.sdk.LDAPConnection;
045import com.unboundid.ldap.sdk.LDAPException;
046import com.unboundid.ldap.sdk.ResultCode;
047import com.unboundid.util.Extensible;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.args.ArgumentException;
051import 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 should 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)
126public 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}