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-2012 UnboundID Corp.
026     */
027    package com.unboundid.directory.sdk.proxy.scripting;
028    
029    
030    
031    import java.util.List;
032    
033    import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034    import com.unboundid.directory.sdk.proxy.config.LDAPHealthCheckConfig;
035    import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
036    import com.unboundid.directory.sdk.proxy.types.BackendServer;
037    import com.unboundid.directory.sdk.proxy.types.CompletedProxyOperationContext;
038    import com.unboundid.directory.sdk.proxy.types.HealthCheckResult;
039    import com.unboundid.directory.sdk.proxy.types.ProxyServerContext;
040    import com.unboundid.ldap.sdk.LDAPConnection;
041    import com.unboundid.ldap.sdk.LDAPException;
042    import com.unboundid.ldap.sdk.ResultCode;
043    import com.unboundid.util.Extensible;
044    import com.unboundid.util.ThreadSafety;
045    import com.unboundid.util.ThreadSafetyLevel;
046    import com.unboundid.util.args.ArgumentException;
047    import 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 not 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)
121    public 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    }