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}