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    }