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.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 }