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