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 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 import java.util.Set;
035
036 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
038 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039 import com.unboundid.directory.sdk.common.operation.ExtendedRequest;
040 import com.unboundid.directory.sdk.common.types.OperationContext;
041 import com.unboundid.directory.sdk.proxy.config.
042 ProxiedExtendedOperationHandlerConfig;
043 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
044 import com.unboundid.directory.sdk.proxy.types.BackendSet;
045 import com.unboundid.directory.sdk.proxy.types.EntryBalancingRequestProcessor;
046 import com.unboundid.directory.sdk.proxy.types.ProxyingRequestProcessor;
047 import com.unboundid.directory.sdk.proxy.types.ProxyServerContext;
048 import com.unboundid.ldap.sdk.ExtendedResult;
049 import com.unboundid.ldap.sdk.LDAPException;
050 import com.unboundid.ldap.sdk.ResultCode;
051 import com.unboundid.util.Extensible;
052 import com.unboundid.util.ObjectPair;
053 import com.unboundid.util.ThreadSafety;
054 import com.unboundid.util.ThreadSafetyLevel;
055 import com.unboundid.util.args.ArgumentException;
056 import com.unboundid.util.args.ArgumentParser;
057
058
059
060 /**
061 * This class defines an API that must be implemented by extensions which are
062 * used to forward extended operations to one or more backend servers. This
063 * API makes it possible to select the backend set(s) to which the operation
064 * should be forwarded, and to aggregate the responses into a single response to
065 * return to the client.
066 * <BR>
067 * <H2>Configuring Proxied Extended Operation Handlers</H2>
068 * In order to configure a proxied extended operation handler created using this
069 * API, use a command like:
070 * <PRE>
071 * dsconfig create-extended-operation-handler \
072 * --handler-name "<I>{handler-name}</I>" \
073 * --type third-party-proxied \
074 * --set enabled:true \
075 * --set "extension-class:<I>{class-name}</I>" \
076 * --set "extension-argument:<I>{name=value}</I>"
077 * </PRE>
078 * where "<I>{handler-name}</I>" is the name to use for the proxied extended
079 * operation handler instance, "<I>{class-name}</I>" is the fully-qualified name
080 * of the Java class that extends {@code
081 * com.unboundid.directory.sdk.proxy.api.ProxiedExtendedOperationHandler}, and
082 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
083 * provide to the proxied extended operation handler. If multiple arguments
084 * should be provided to the proxied extended operation handler, then the
085 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
086 * provided multiple times.
087 */
088 @Extensible()
089 @DirectoryProxyServerExtension(appliesToLocalContent=false,
090 appliesToRemoteContent=true)
091 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
092 public abstract class ProxiedExtendedOperationHandler
093 implements UnboundIDExtension,
094 Reconfigurable<ProxiedExtendedOperationHandlerConfig>,
095 ExampleUsageProvider
096 {
097 /**
098 * Creates a new instance of this proxied extended operation handler. All
099 * proxied extended operation handler implementations must include a default
100 * constructor, but any initialization should generally be done in the
101 * {@code initializeProxiedExtendedOperationHandler} method.
102 */
103 public ProxiedExtendedOperationHandler()
104 {
105 // No implementation is required.
106 }
107
108
109
110 /**
111 * {@inheritDoc}
112 */
113 @Override()
114 public abstract String getExtensionName();
115
116
117
118 /**
119 * {@inheritDoc}
120 */
121 @Override()
122 public abstract String[] getExtensionDescription();
123
124
125
126 /**
127 * {@inheritDoc}
128 */
129 @Override()
130 public void defineConfigArguments(final ArgumentParser parser)
131 throws ArgumentException
132 {
133 // No arguments will be allowed by default.
134 }
135
136
137
138 /**
139 * Initializes this proxied extended operation handler.
140 *
141 * @param serverContext A handle to the server context for the server in
142 * which this extension is running.
143 * @param config The general configuration for this extended
144 * operation handler.
145 * @param parser The argument parser which has been initialized from
146 * the configuration for this extended operation
147 * handler.
148 *
149 * @throws LDAPException If a problem occurs while initializing this
150 * extended operation handler.
151 */
152 public void initializeProxiedExtendedOperationHandler(
153 final ProxyServerContext serverContext,
154 final ProxiedExtendedOperationHandlerConfig config,
155 final ArgumentParser parser)
156 throws LDAPException
157 {
158 // No initialization will be performed by default.
159 }
160
161
162
163 /**
164 * {@inheritDoc}
165 */
166 @Override()
167 public boolean isConfigurationAcceptable(
168 final ProxiedExtendedOperationHandlerConfig config,
169 final ArgumentParser parser,
170 final List<String> unacceptableReasons)
171 {
172 // No extended validation will be performed by default.
173 return true;
174 }
175
176
177
178 /**
179 * {@inheritDoc}
180 */
181 @Override()
182 public ResultCode applyConfiguration(
183 final ProxiedExtendedOperationHandlerConfig config,
184 final ArgumentParser parser,
185 final List<String> adminActionsRequired,
186 final List<String> messages)
187 {
188 // By default, no configuration changes will be applied. If there are any
189 // arguments, then add an admin action message indicating that the extension
190 // needs to be restarted for any changes to take effect.
191 if (! parser.getNamedArguments().isEmpty())
192 {
193 adminActionsRequired.add(
194 "No configuration change has actually been applied. The new " +
195 "configuration will not take effect until this proxied " +
196 "extended operation handler is disabled and re-enabled or " +
197 "until the server is restarted.");
198 }
199
200 return ResultCode.SUCCESS;
201 }
202
203
204
205 /**
206 * Performs any cleanup which may be necessary when this proxied extended
207 * operation handler is to be taken out of service.
208 */
209 public void finalizeProxiedExtendedOperationHandler()
210 {
211 // No implementation is required.
212 }
213
214
215
216 /**
217 * Retrieves the name of the extended operation with the provided OID.
218 *
219 * @param oid The OID of the extended operation for which to retrieve the
220 * corresponding name.
221 *
222 * @return The name of the extended operation with the specified OID, or
223 * {@code null} if the specified OID is not recognized by this
224 * proxied extended operation handler.
225 */
226 public abstract String getExtendedOperationName(final String oid);
227
228
229
230 /**
231 * Retrieves the OIDs of the extended operation types supported by this
232 * proxied extended operation handler.
233 *
234 * @return The OIDs of the extended operation types supported by this proxied
235 * extended operation handler. It must not be {@code null} or
236 * empty, and the contents of the set returned must not change over
237 * the life of this proxied extended operation handler.
238 */
239 public abstract Set<String> getSupportedExtensions();
240
241
242
243 /**
244 * Retrieves the OIDs of any controls supported by this proxied extended
245 * operation handler.
246 *
247 * @return The OIDs of any controls supported by this proxied extended
248 * operation handler. It may be {@code null} or empty if this
249 * proxied extended operation handler does not support any controls.
250 */
251 public Set<String> getSupportedControls()
252 {
253 return Collections.emptySet();
254 }
255
256
257
258 /**
259 * Retrieves the OIDs of any features supported by this proxied extended
260 * operation handler that should be advertised in the server root DSE.
261 *
262 * @return The OIDs of any features supported by this proxied extended
263 * operation handler. It may be {@code null} or empty if this
264 * proxied extended operation handler does not support any features.
265 */
266 public Set<String> getSupportedFeatures()
267 {
268 return Collections.emptySet();
269 }
270
271
272
273 /**
274 * Selects the entry-balancing backend set(s) to which the provided extended
275 * request should be forwarded. This method will only be invoked for
276 * operations in which the requester's client connection policy includes one
277 * or more subtree views which reference an entry-balancing request processor.
278 * <BR><BR>
279 * This method returns two groups of backend sets, with the first representing
280 * an initial guess (e.g., based on information obtained from the
281 * entry-balancing global index), and the second representing a fallback if
282 * the initial guess was found to be incorrect.
283 * <BR><BR>
284 * If it can be determined that no backend sets associated with the provided
285 * entry-balancing request processor should be used to process the extended
286 * operation, then the object returned may have both elements set to
287 * {@code null} or empty sets. If it is possible to definitively determine
288 * the set of backend sets to which the operation should be forwarded and no
289 * fallback option is required, then the first element of the returned object
290 * should be populated with a non-empty set and the second element should be
291 * {@code null} or empty.
292 *
293 * @param operationContext The operation context for the extended operation.
294 * @param request The extended request to be processed.
295 * @param requestProcessor The entry-balancing request processor in which
296 * the extended operation should be processed.
297 *
298 * @return The set of backend sets to which the request should be forwarded.
299 * It may be {@code null} (or it may be an {@code ObjectPair} with
300 * both elements empty or {@code null}) if the request should not be
301 * forwarded to any backend set for the entry-balancing request
302 * processor.
303 *
304 * @throws LDAPException If the request should not be forwarded to any of
305 * the backend sets for the provided entry-balancing
306 * request processor, and the result contained in the
307 * exception should be used instead.
308 */
309 public abstract ObjectPair<Set<BackendSet>,Set<BackendSet>> selectBackendSets(
310 final OperationContext operationContext,
311 final ExtendedRequest request,
312 final EntryBalancingRequestProcessor requestProcessor)
313 throws LDAPException;
314
315
316
317 /**
318 * Obtains the extended result that should be used as a result of processing
319 * an operation in one or more entry-balanced backend sets, or throws an
320 * exception to indicate that the request should instead be forwarded to the
321 * fallback server set(s).
322 * <BR><BR>
323 * This method will only be invoked for cases in which the
324 * {@link #selectBackendSets} method indicates that multiple backend sets
325 * should be accessed in the course of processing an extended request.
326 *
327 * @param operationContext The operation context for the extended
328 * operation.
329 * @param request The extended request that was processed.
330 * @param requestProcessor The entry-balancing request processor in which
331 * the extended operation was processed.
332 * @param results A list of the extended results obtained from
333 * processing the operation in the selected set of
334 * backend sets, with each result paired with
335 * information about the backend set from which it
336 * was obtained.
337 * @param fallbackAvailable Indicates whether a fallback group of backend
338 * sets is available and may be used as a second
339 * attempt at processing the operation if the
340 * result from the initial attempt is not
341 * acceptable.
342 *
343 * @return An extended result that represents the merged result from the
344 * provided list of results. It must not be {@code null}.
345 *
346 * @throws LDAPException To indicate that the initial set of results was not
347 * acceptable and that the operation should instead be
348 * forwarded to the fallback group of backend sets.
349 * If no fallback set of results is available, then an
350 * extended result will be generated from the content
351 * of this exception.
352 */
353 public abstract ExtendedResult mergeEntryBalancedResults(
354 final OperationContext operationContext,
355 final ExtendedRequest request,
356 final EntryBalancingRequestProcessor requestProcessor,
357 final List<ObjectPair<ExtendedResult,BackendSet>> results,
358 final boolean fallbackAvailable)
359 throws LDAPException;
360
361
362
363 /**
364 * Indicates whether the provided extended request should be forwarded to one
365 * of the servers associated with the provided proxying request processor.
366 * Note that this method will not be invoked for proxying request processors
367 * associated with an entry-balancing request processor.
368 *
369 * @param operationContext The operation context for the extended operation.
370 * @param request The extended request to be processed.
371 * @param requestProcessor The proxying request processor for which to
372 * make the determination.
373 *
374 * @return {@code true} if the extended request should be forwarded to one of
375 * the servers associated with the proxying request processor, or
376 * {@code false} if not.
377 *
378 * @throws LDAPException If the request should not be forwarded to a
379 * backend server associated with the proxying request
380 * processor, but the result contained in the
381 * exception should be used instead.
382 */
383 public abstract boolean shouldForward(final OperationContext operationContext,
384 final ExtendedRequest request,
385 final ProxyingRequestProcessor requestProcessor)
386 throws LDAPException;
387
388
389
390 /**
391 * Creates the final extended result to return to the client from the provided
392 * list of results obtained from the set of entry-balanced and/or proxying
393 * request processors to which the request was forwarded.
394 *
395 * @param operationContext The operation context for the extended
396 * operation.
397 * @param request The extended request that was processed.
398 * @param results The results from all request processors to which
399 * the request was forwarded. It may be empty if
400 * the request was not forwarded to any backend
401 * servers, in which case this method must
402 * construct an appropriate result. It may have
403 * only a single element if the request was only
404 * forwarded to one server, and in many cases it
405 * may be desirable to simply use that result as
406 * the final result. It may also have multiple
407 * elements if the request was forwarded to
408 * multiple backend servers, in which case this
409 * method must determine whether to return one of
410 * them to the client, or to construct a new result
411 * to return instead.
412 *
413 * @return The final extended result to be returned to the client.
414 */
415 public abstract ExtendedResult createFinalResult(
416 final OperationContext operationContext,
417 final ExtendedRequest request,
418 final List<ExtendedResult> results);
419
420
421
422 /**
423 * {@inheritDoc}
424 */
425 @Override()
426 public Map<List<String>,String> getExamplesArgumentSets()
427 {
428 return Collections.emptyMap();
429 }
430 }