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-2024 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.sync.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig;
039import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
040import com.unboundid.directory.sdk.sync.scripting.
041            ScriptedLDAPSyncDestinationPlugin;
042import com.unboundid.directory.sdk.sync.types.PostStepResult;
043import com.unboundid.directory.sdk.sync.types.PreStepResult;
044import com.unboundid.directory.sdk.sync.types.SyncOperation;
045import com.unboundid.directory.sdk.sync.types.SyncServerContext;
046import com.unboundid.ldap.sdk.Entry;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.LDAPInterface;
049import com.unboundid.ldap.sdk.Modification;
050import com.unboundid.ldap.sdk.ResultCode;
051import com.unboundid.ldap.sdk.SearchRequest;
052import com.unboundid.ldap.sdk.UpdatableLDAPRequest;
053import com.unboundid.util.Extensible;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.args.ArgumentException;
057import com.unboundid.util.args.ArgumentParser;
058
059
060
061/**
062 * This class defines an API that must be implemented by extensions that
063 * perform processing on synchronization operations within an LDAP Sync
064 * Destination.  These extensions may be used to
065 * <ul>
066 *   <li>Filter out certain changes from being synchronized.</li>
067 *   <li>Change how an entry is fetched.</li>
068 *   <li>Change how an entry is modified or created.</li>
069 * </ul>
070 * <BR>
071 * A note on exception handling: in general subclasses should not
072 * catch LDAPExceptions that are thrown when using the provided
073 * LDAPInterface unless there are specific exceptions that are
074 * expected.  The Data Sync Server will handle
075 * LDAPExceptions in an appropriate way based on the specific
076 * cause of the exception.  For example, some errors will result
077 * in the SyncOperation being retried, and others will trigger
078 * fail over to a different server.
079 * <BR>
080 * <H2>Configuring LDAP Sync Destination Plugins</H2>
081 * In order to configure an LDAP sync destination plugin created using this API,
082 * use a command like:
083 * <PRE>
084 *      dsconfig create-sync-destination-plugin \
085 *           --plugin-name "<I>{plugin-name}</I>" \
086 *           --type third-party-ldap \
087 *           --set "extension-class:<I>{class-name}</I>" \
088 *           --set "extension-argument:<I>{name=value}</I>"
089 * </PRE>
090 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync destination
091 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the
092 * Java class that extends
093 * {@code com.unboundid.directory.sdk.sync.api.LDAPSyncDestinationPlugin}, and
094 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
095 * provide to the LDAP sync destination plugin.  If multiple arguments should be
096 * provided to the LDAP sync destination plugin, then the
097 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
098 * provided multiple times.
099 *
100 * @see  ScriptedLDAPSyncDestinationPlugin
101 */
102@Extensible()
103@SynchronizationServerExtension(appliesToLocalContent=false,
104     appliesToSynchronizedContent=true)
105@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
106public abstract class LDAPSyncDestinationPlugin
107       implements UnboundIDExtension,
108                  Reconfigurable<LDAPSyncDestinationPluginConfig>,
109                  ExampleUsageProvider
110{
111  /**
112   * Creates a new instance of this LDAP sync destination plugin.  All sync
113   * destination implementations must include a default constructor, but any
114   * initialization should generally be done in the
115   * {@code initializeLDAPSyncDestinationPlugin} method.
116   */
117  public LDAPSyncDestinationPlugin()
118  {
119    // No implementation is required.  However, we need to reference the
120    // scripted LDAP sync destination plugin so that checkstyle is satisfied
121    // with the import.
122    final ScriptedLDAPSyncDestinationPlugin scriptedPlugin = null;
123  }
124
125
126
127  /**
128   * {@inheritDoc}
129   */
130  public abstract String getExtensionName();
131
132
133
134  /**
135   * {@inheritDoc}
136   */
137  public abstract String[] getExtensionDescription();
138
139
140
141  /**
142   * {@inheritDoc}
143   */
144  public void defineConfigArguments(final ArgumentParser parser)
145         throws ArgumentException
146  {
147    // No arguments will be allowed by default.
148  }
149
150
151
152  /**
153   * Initializes this LDAP sync destination plugin.  This method will be called
154   * before any other methods in the class.
155   *
156   * @param  serverContext  A handle to the server context for the
157   *                        Data Sync Server in which this extension is
158   *                        running.  Extensions should typically store this
159   *                        in a class member.
160   * @param  config         The general configuration for this proxy
161   *                        transformation.
162   * @param  parser         The argument parser which has been initialized from
163   *                        the configuration for this LDAP sync destination
164   *                        plugin.
165   *
166   * @throws  LDAPException  If a problem occurs while initializing this ldap
167   *                         sync destination plugin.
168   */
169  public void initializeLDAPSyncDestinationPlugin(
170                   final SyncServerContext serverContext,
171                   final LDAPSyncDestinationPluginConfig config,
172                   final ArgumentParser parser)
173         throws LDAPException
174  {
175    // No initialization will be performed by default.
176  }
177
178
179
180  /**
181   * {@inheritDoc}
182   */
183  public boolean isConfigurationAcceptable(
184                      final LDAPSyncDestinationPluginConfig config,
185                      final ArgumentParser parser,
186                      final List<String> unacceptableReasons)
187  {
188    // No extended validation will be performed by default.
189    return true;
190  }
191
192
193
194  /**
195   * {@inheritDoc}
196   */
197  public ResultCode applyConfiguration(
198                         final LDAPSyncDestinationPluginConfig 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.
204    return ResultCode.SUCCESS;
205  }
206
207
208
209  /**
210   * Performs any cleanup which may be necessary when this LDAP sync destination
211   * plugin is taken out of service.  This can happen when it is deleted from
212   * the configuration and at server shutdown.
213   */
214  public void finalizeLDAPSyncDestinationPlugin()
215  {
216    // No implementation is required.
217  }
218
219
220
221  /**
222   * This method is called before a destination entry is fetched.  A
223   * connection to the destination server is provided along with the
224   * {@code SearchRequest} that will be sent to the server.  This method is
225   * overridden by plugins that need to have access to the search request
226   * before it is sent to the destination server.  This includes updating the
227   * search request as well as performing the search instead of the core server,
228   * including doing additional searches.  For plugins that need to manipulate
229   * the entries that the core LDAP Sync Destination code retrieves from the
230   * destination, implementing the {@link #postFetch} method is more natural.
231   * <p>
232   * This method might be called multiple times for a single synchronization
233   * operation, specifically when there are multiple search criteria or
234   * multiple base DNs defined for the Sync Destination.
235   *
236   * @param  destinationConnection  A connection to the destination server.
237   * @param  searchRequest          The search request that the LDAP Sync
238   *                                Destination will use to fetch the entry.
239   * @param  fetchedEntries         A list of entries that have been fetched.
240   *                                When the search criteria matches multiple
241   *                                entries, they should all be returned.  A
242   *                                plugin that wishes to implement the fetch
243   *                                should put the fetched entries here and
244   *                                return
245   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
246   * @param  operation              The synchronization operation for this
247   *                                change.
248   *
249   * @return  The result of the plugin processing. Be very careful when
250   *          returning {@code PreStepResult#RETRY_OPERATION_UNLIMITED} as this
251   *          can stall all in flight operations until this operation completes.
252   *          This return value should only be used in situations where a
253   *          remote service (e.g., the LDAP server) is unavailable. In this
254   *          case, it's preferable to just throw the underlying LDAPException,
255   *          which the Data Sync Server will handle correctly based on
256   *          the type of the operation. Note:
257   *          {@code PreStepResult#SKIP_CURRENT_STEP} should only be returned
258   *          if this plugin takes responsibility for fully fetching the entry
259   *          according to the search request and for populating the
260   *          fetched entry list.
261   *
262   * @throws  LDAPException  In general subclasses should not catch
263   *                         LDAPExceptions that are thrown when
264   *                         using the LDAPInterface unless there
265   *                         are specific exceptions that are
266   *                         expected.  The Data Sync Server
267   *                         will handle LDAPExceptions in an
268   *                         appropriate way based on the specific
269   *                         cause of the exception.  For example,
270   *                         some errors will result in the
271   *                         SyncOperation being retried, and others
272   *                         will trigger fail over to a different
273   *                         server.  Plugins should only throw
274   *                         LDAPException for errors related to
275   *                         communication with the LDAP server.
276   *                         Use the return code to indicate other
277   *                         types of errors, which might require
278   *                         retry.
279   */
280  public PreStepResult preFetch(final LDAPInterface destinationConnection,
281                                final SearchRequest searchRequest,
282                                final List<Entry> fetchedEntries,
283                                final SyncOperation operation)
284       throws LDAPException
285  {
286    return PreStepResult.CONTINUE;
287  }
288
289
290
291  /**
292   * This method is called after an attempt to fetch a destination entry.  An
293   * connection to the destination server is provided along with the
294   * {@code SearchRequest} that was sent to the server.  This method is
295   * overridden by plugins that need to manipulate the search results that
296   * are returned to the Sync Pipe.  This can include filtering out certain
297   * entries, remove information from the entries, or adding additional
298   * information, possibly by doing a followup LDAP search.
299   * <p>
300   * This method might be called multiple times for a single synchronization
301   * operation, specifically when there are multiple search criteria or
302   * multiple base DNs defined for the Sync Destination.
303   * <p>
304   * This method will not be called if the search fails, for instance, if
305   * the base DN of the search does not exist.
306   *
307   * @param  destinationConnection  A connection to the destination server.
308   * @param  searchRequest          The search request that the LDAP Sync
309   *                                Destination used to fetch the entry.
310   * @param  fetchedEntries         A list of entries that have been fetched.
311   *                                When the search criteria matches multiple
312   *                                entries, they will all be returned.  Entries
313   *                                in this list can be edited directly, and the
314   *                                list can be edited as well.
315   * @param  operation              The synchronization operation for this
316   *                                change.
317   *
318   * @return  The result of the plugin processing. Be very careful when
319   *          returning {@code PostStepResult#RETRY_OPERATION_UNLIMITED} as this
320   *          can stall all in flight operations until this operation completes.
321   *          This return value should only be used in situations where a
322   *          remote service (e.g., the LDAP server) is unavailable. In this
323   *          case, it's preferable to just throw the underlying LDAPException,
324   *          which the Data Sync Server will handle correctly based on
325   *          the type of the operation.
326   *
327   * @throws  LDAPException  In general subclasses should not catch
328   *                         LDAPExceptions that are thrown when
329   *                         using the LDAPInterface unless there
330   *                         are specific exceptions that are
331   *                         expected.  The Data Sync Server
332   *                         will handle LDAPExceptions in an
333   *                         appropriate way based on the specific
334   *                         cause of the exception.  For example,
335   *                         some errors will result in the
336   *                         SyncOperation being retried, and others
337   *                         will trigger fail over to a different
338   *                         server.  Plugins should only throw
339   *                         LDAPException for errors related to
340   *                         communication with the LDAP server.
341   *                         Use the return code to indicate other
342   *                         types of errors, which might require
343   *                         retry.
344   */
345  public PostStepResult postFetch(final LDAPInterface destinationConnection,
346                                  final SearchRequest searchRequest,
347                                  final List<Entry> fetchedEntries,
348                                  final SyncOperation operation)
349       throws LDAPException
350  {
351    return PostStepResult.CONTINUE;
352  }
353
354
355
356  /**
357   * This method is called before a destination entry is created.  A
358   * connection to the destination server is provided along with the
359   * {@code Entry} that will be sent to the server.  This method is
360   * overridden by plugins that need to alter the entry before it is created
361   * at the server.
362   *
363   * @param  destinationConnection  A connection to the destination server.
364   * @param  entryToCreate          The entry that will be created at the
365   *                                destination.  A plugin that wishes to
366   *                                create the entry should be sure to return
367   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
368   * @param  operation              The synchronization operation for this
369   *                                change.
370   *
371   * @return  The result of the plugin processing. Be very careful when
372   *          returning {@code PreStepResult#RETRY_OPERATION_UNLIMITED} as this
373   *          can stall all in flight operations until this operation completes.
374   *          This return value should only be used in situations where a
375   *          remote service (e.g., the LDAP server) is unavailable. In this
376   *          case, it's preferable to just throw the underlying LDAPException,
377   *          which the Data Sync Server will handle correctly based on
378   *          the type of the operation.
379   *
380   * @throws  LDAPException  In general subclasses should not catch
381   *                         LDAPExceptions that are thrown when
382   *                         using the LDAPInterface unless there
383   *                         are specific exceptions that are
384   *                         expected.  The Data Sync Server
385   *                         will handle LDAPExceptions in an
386   *                         appropriate way based on the specific
387   *                         cause of the exception.  For example,
388   *                         some errors will result in the
389   *                         SyncOperation being retried, and others
390   *                         will trigger fail over to a different
391   *                         server.  Plugins should only throw
392   *                         LDAPException for errors related to
393   *                         communication with the LDAP server.
394   *                         Use the return code to indicate other
395   *                         types of errors, which might require
396   *                         retry.
397   */
398  public PreStepResult preCreate(final LDAPInterface destinationConnection,
399                                 final Entry entryToCreate,
400                                 final SyncOperation operation)
401       throws LDAPException
402  {
403    return PreStepResult.CONTINUE;
404  }
405
406
407
408  /**
409   * This method is called before a destination entry is modified.  A
410   * connection to the destination server is provided along with the
411   * {@code Entry} that will be sent to the server.  This method is
412   * overridden by plugins that need to perform some processing on an entry
413   * before it is modified.
414   *
415   * @param  destinationConnection  A connection to the destination server.
416   * @param  entryToModify          The entry that will be modified at the
417   *                                destination.  A plugin that wishes to
418   *                                modify the entry should be sure to return
419   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
420   * @param  modsToApply            A modifiable list of the modifications to
421   *                                apply at the server.
422   * @param  operation              The synchronization operation for this
423   *                                change.
424   *
425   * @return  The result of the plugin processing. Be very careful when
426   *          returning {@code PreStepResult#RETRY_OPERATION_UNLIMITED} as this
427   *          can stall all in flight operations until this operation completes.
428   *          This return value should only be used in situations where a
429   *          remote service (e.g., the LDAP server) is unavailable. In this
430   *          case, it's preferable to just throw the underlying LDAPException,
431   *          which the Data Sync Server will handle correctly based on
432   *          the type of the operation.
433   *
434   * @throws  LDAPException  In general subclasses should not catch
435   *                         LDAPExceptions that are thrown when
436   *                         using the LDAPInterface unless there
437   *                         are specific exceptions that are
438   *                         expected.  The Data Sync Server
439   *                         will handle LDAPExceptions in an
440   *                         appropriate way based on the specific
441   *                         cause of the exception.  For example,
442   *                         some errors will result in the
443   *                         SyncOperation being retried, and others
444   *                         will trigger fail over to a different
445   *                         server.  Plugins should only throw
446   *                         LDAPException for errors related to
447   *                         communication with the LDAP server.
448   *                         Use the return code to indicate other
449   *                         types of errors, which might require
450   *                         retry.
451   */
452  public PreStepResult preModify(final LDAPInterface destinationConnection,
453                                 final Entry entryToModify,
454                                 final List<Modification> modsToApply,
455                                 final SyncOperation operation)
456       throws LDAPException
457  {
458    return PreStepResult.CONTINUE;
459  }
460
461
462
463  /**
464   * This method is called before a destination entry is deleted.  A
465   * connection to the destination server is provided along with the
466   * {@code Entry} that will be sent to the server.  This method is
467   * overridden by plugins that need to perform some processing on an entry
468   * before it is deleted.  A plugin could choose to mark an entry as disabled
469   * instead of deleting it for instance, or move the entry to a different
470   * part of the directory hierarchy.
471   *
472   * @param  destinationConnection  A connection to the destination server.
473   * @param  entryToDelete          The entry that will be deleted at the
474   *                                destination.  A plugin that wishes to
475   *                                delete the entry should be sure to return
476   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
477   * @param  operation              The synchronization operation for this
478   *                                change.
479   *
480   * @return  The result of the plugin processing. Be very careful when
481   *          returning {@code PreStepResult#RETRY_OPERATION_UNLIMITED} as this
482   *          can stall all in flight operations until this operation completes.
483   *          This return value should only be used in situations where a
484   *          remote service (e.g., the LDAP server) is unavailable. In this
485   *          case, it's preferable to just throw the underlying LDAPException,
486   *          which the Data Sync Server will handle correctly based on
487   *          the type of the operation.
488   *
489   * @throws  LDAPException  In general subclasses should not catch
490   *                         LDAPExceptions that are thrown when
491   *                         using the LDAPInterface unless there
492   *                         are specific exceptions that are
493   *                         expected.  The Data Sync Server
494   *                         will handle LDAPExceptions in an
495   *                         appropriate way based on the specific
496   *                         cause of the exception.  For example,
497   *                         some errors will result in the
498   *                         SyncOperation being retried, and others
499   *                         will trigger fail over to a different
500   *                         server.  Plugins should only throw
501   *                         LDAPException for errors related to
502   *                         communication with the LDAP server.
503   *                         Use the return code to indicate other
504   *                         types of errors, which might require
505   *                         retry.
506   */
507  public PreStepResult preDelete(final LDAPInterface destinationConnection,
508                                 final Entry entryToDelete,
509                                 final SyncOperation operation)
510       throws LDAPException
511  {
512    return PreStepResult.CONTINUE;
513  }
514
515
516
517  /**
518   * This method is called prior to executing any add, modify, delete, or
519   * search from the destination but after the respective pre method (e.g
520   * preFetch or preModify). A connection to the destination server is provided
521   * along with the {@code UpdatableLDAPRequest} that will be sent to the
522   * server. this method is overridden by plugins that need to modify the
523   * LDAP request prior to execution. For example, attaching a {@code Control}
524   * to the request. Callers of this method can use {@code instanceof}
525   * to determine which type of LDAP request is being made.
526   *
527   * @param destinationConnection A connection to the destination server.
528   * @param request               The LDAP request that will be sent to
529   *                              the destination server.
530   * @param operation             The synchronization operation for this
531   *                              change.
532   *
533   * @return  The result of the plugin processing. Be very careful when
534   *          returning {@code PreStepResult#RETRY_OPERATION_UNLIMITED} as this
535   *          can stall all in flight operations until this operation completes.
536   *          This return value should only be used in situations where a
537   *          remote service (e.g., the LDAP server) is unavailable. In this
538   *          case, it's preferable to just throw the underlying LDAPException,
539   *          which the Data Sync Server will handle correctly based on
540   *          the type of the operation.
541   *
542   * @throws  LDAPException  In general subclasses should not catch
543   *                         LDAPExceptions that are thrown when
544   *                         using the LDAPInterface unless there
545   *                         are specific exceptions that are
546   *                         expected.  The Data Sync Server
547   *                         will handle LDAPExceptions in an
548   *                         appropriate way based on the specific
549   *                         cause of the exception.  For example,
550   *                         some errors will result in the
551   *                         SyncOperation being retried, and others
552   *                         will trigger fail over to a different
553   *                         server.  Plugins should only throw
554   *                         LDAPException for errors related to
555   *                         communication with the LDAP server.
556   *                         Use the return code to indicate other
557   *                         types of errors, which might require
558   *                         retry.
559   */
560  public PreStepResult transformRequest(
561          final LDAPInterface destinationConnection,
562          final UpdatableLDAPRequest request,
563          final SyncOperation operation)
564    throws LDAPException
565  {
566    return PreStepResult.CONTINUE;
567  }
568
569
570
571  /**
572   * Retrieves a string representation of this LDAP sync destination plugin.
573   *
574   * @return  A string representation of this LDAP sync destination plugin.
575   */
576  @Override()
577  public final String toString()
578  {
579    final StringBuilder buffer = new StringBuilder();
580    toString(buffer);
581    return buffer.toString();
582  }
583
584
585
586  /**
587   * Appends a string representation of this LDAP sync destination plugin to the
588   * provided buffer.
589   *
590   * @param  buffer  The buffer to which the string representation should be
591   *                 appended.
592   */
593  public abstract void toString(final StringBuilder buffer);
594
595
596
597  /**
598   * {@inheritDoc}
599   */
600  public Map<List<String>,String> getExamplesArgumentSets()
601  {
602    return Collections.emptyMap();
603  }
604}