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-2015 UnboundID Corp.
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.  Note:
249   *          {@code PreStepResult#SKIP_CURRENT_STEP} should only be returned
250   *          if this plugin takes responsibility for fully fetching the entry
251   *          according to the search request and for populating the
252   *          fetched entry list.
253   *
254   * @throws  LDAPException  In general subclasses should not catch
255   *                         LDAPExceptions that are thrown when
256   *                         using the LDAPInterface unless there
257   *                         are specific exceptions that are
258   *                         expected.  The Synchronization Server
259   *                         will handle LDAPExceptions in an
260   *                         appropriate way based on the specific
261   *                         cause of the exception.  For example,
262   *                         some errors will result in the
263   *                         SyncOperation being retried, and others
264   *                         will trigger fail over to a different
265   *                         server.  Plugins should only throw
266   *                         LDAPException for errors related to
267   *                         communication with the LDAP server.
268   *                         Use the return code to indicate other
269   *                         types of errors, which might require
270   *                         retry.
271   */
272  public PreStepResult preFetch(final LDAPInterface destinationConnection,
273                                final SearchRequest searchRequest,
274                                final List<Entry> fetchedEntries,
275                                final SyncOperation operation)
276       throws LDAPException
277  {
278    return PreStepResult.CONTINUE;
279  }
280
281
282
283  /**
284   * This method is called after an attempt to fetch a destination entry.  An
285   * connection to the destination server is provided along with the
286   * {@code SearchRequest} that was sent to the server.  This method is
287   * overridden by plugins that need to manipulate the search results that
288   * are returned to the Sync Pipe.  This can include filtering out certain
289   * entries, remove information from the entries, or adding additional
290   * information, possibly by doing a followup LDAP search.
291   * <p>
292   * This method might be called multiple times for a single synchronization
293   * operation, specifically when there are multiple search criteria or
294   * multiple base DNs defined for the Sync Destination.
295   * <p>
296   * This method will not be called if the search fails, for instance, if
297   * the base DN of the search does not exist.
298   *
299   * @param  destinationConnection  A connection to the destination server.
300   * @param  searchRequest          The search request that the LDAP Sync
301   *                                Destination used to fetch the entry.
302   * @param  fetchedEntries         A list of entries that have been fetched.
303   *                                When the search criteria matches multiple
304   *                                entries, they will all be returned.  Entries
305   *                                in this list can be edited directly, and the
306   *                                list can be edited as well.
307   * @param  operation              The synchronization operation for this
308   *                                change.
309   *
310   * @return  The result of the plugin processing.
311   *
312   * @throws  LDAPException  In general subclasses should not catch
313   *                         LDAPExceptions that are thrown when
314   *                         using the LDAPInterface unless there
315   *                         are specific exceptions that are
316   *                         expected.  The Synchronization Server
317   *                         will handle LDAPExceptions in an
318   *                         appropriate way based on the specific
319   *                         cause of the exception.  For example,
320   *                         some errors will result in the
321   *                         SyncOperation being retried, and others
322   *                         will trigger fail over to a different
323   *                         server.  Plugins should only throw
324   *                         LDAPException for errors related to
325   *                         communication with the LDAP server.
326   *                         Use the return code to indicate other
327   *                         types of errors, which might require
328   *                         retry.
329   */
330  public PostStepResult postFetch(final LDAPInterface destinationConnection,
331                                  final SearchRequest searchRequest,
332                                  final List<Entry> fetchedEntries,
333                                  final SyncOperation operation)
334       throws LDAPException
335  {
336    return PostStepResult.CONTINUE;
337  }
338
339
340
341  /**
342   * This method is called before a destination entry is created.  A
343   * connection to the destination server is provided along with the
344   * {@code Entry} that will be sent to the server.  This method is
345   * overridden by plugins that need to alter the entry before it is created
346   * at the server.
347   *
348   * @param  destinationConnection  A connection to the destination server.
349   * @param  entryToCreate          The entry that will be created at the
350   *                                destination.  A plugin that wishes to
351   *                                create the entry should be sure to return
352   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
353   * @param  operation              The synchronization operation for this
354   *                                change.
355   *
356   * @return  The result of the plugin processing.
357   *
358   * @throws  LDAPException  In general subclasses should not catch
359   *                         LDAPExceptions that are thrown when
360   *                         using the LDAPInterface unless there
361   *                         are specific exceptions that are
362   *                         expected.  The Synchronization Server
363   *                         will handle LDAPExceptions in an
364   *                         appropriate way based on the specific
365   *                         cause of the exception.  For example,
366   *                         some errors will result in the
367   *                         SyncOperation being retried, and others
368   *                         will trigger fail over to a different
369   *                         server.  Plugins should only throw
370   *                         LDAPException for errors related to
371   *                         communication with the LDAP server.
372   *                         Use the return code to indicate other
373   *                         types of errors, which might require
374   *                         retry.
375   */
376  public PreStepResult preCreate(final LDAPInterface destinationConnection,
377                                 final Entry entryToCreate,
378                                 final SyncOperation operation)
379       throws LDAPException
380  {
381    return PreStepResult.CONTINUE;
382  }
383
384
385
386  /**
387   * This method is called before a destination entry is modified.  A
388   * connection to the destination server is provided along with the
389   * {@code Entry} that will be sent to the server.  This method is
390   * overridden by plugins that need to perform some processing on an entry
391   * before it is modified.
392   *
393   * @param  destinationConnection  A connection to the destination server.
394   * @param  entryToModify          The entry that will be modified at the
395   *                                destination.  A plugin that wishes to
396   *                                modify the entry should be sure to return
397   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
398   * @param  modsToApply            A modifiable list of the modifications to
399   *                                apply at the server.
400   * @param  operation              The synchronization operation for this
401   *                                change.
402   *
403   * @return  The result of the plugin processing.
404   *
405   * @throws  LDAPException  In general subclasses should not catch
406   *                         LDAPExceptions that are thrown when
407   *                         using the LDAPInterface unless there
408   *                         are specific exceptions that are
409   *                         expected.  The Synchronization Server
410   *                         will handle LDAPExceptions in an
411   *                         appropriate way based on the specific
412   *                         cause of the exception.  For example,
413   *                         some errors will result in the
414   *                         SyncOperation being retried, and others
415   *                         will trigger fail over to a different
416   *                         server.  Plugins should only throw
417   *                         LDAPException for errors related to
418   *                         communication with the LDAP server.
419   *                         Use the return code to indicate other
420   *                         types of errors, which might require
421   *                         retry.
422   */
423  public PreStepResult preModify(final LDAPInterface destinationConnection,
424                                 final Entry entryToModify,
425                                 final List<Modification> modsToApply,
426                                 final SyncOperation operation)
427       throws LDAPException
428  {
429    return PreStepResult.CONTINUE;
430  }
431
432
433
434  /**
435   * This method is called before a destination entry is deleted.  A
436   * connection to the destination server is provided along with the
437   * {@code Entry} that will be sent to the server.  This method is
438   * overridden by plugins that need to perform some processing on an entry
439   * before it is deleted.  A plugin could choose to mark an entry as disabled
440   * instead of deleting it for instance, or move the entry to a different
441   * part of the directory hierarchy.
442   *
443   * @param  destinationConnection  A connection to the destination server.
444   * @param  entryToDelete          The entry that will be deleted at the
445   *                                destination.  A plugin that wishes to
446   *                                delete the entry should be sure to return
447   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
448   * @param  operation              The synchronization operation for this
449   *                                change.
450   *
451   * @return  The result of the plugin processing.
452   *
453   * @throws  LDAPException  In general subclasses should not catch
454   *                         LDAPExceptions that are thrown when
455   *                         using the LDAPInterface unless there
456   *                         are specific exceptions that are
457   *                         expected.  The Synchronization Server
458   *                         will handle LDAPExceptions in an
459   *                         appropriate way based on the specific
460   *                         cause of the exception.  For example,
461   *                         some errors will result in the
462   *                         SyncOperation being retried, and others
463   *                         will trigger fail over to a different
464   *                         server.  Plugins should only throw
465   *                         LDAPException for errors related to
466   *                         communication with the LDAP server.
467   *                         Use the return code to indicate other
468   *                         types of errors, which might require
469   *                         retry.
470   */
471  public PreStepResult preDelete(final LDAPInterface destinationConnection,
472                                 final Entry entryToDelete,
473                                 final SyncOperation operation)
474       throws LDAPException
475  {
476    return PreStepResult.CONTINUE;
477  }
478
479
480
481  /**
482   * Retrieves a string representation of this LDAP sync destination plugin.
483   *
484   * @return  A string representation of this LDAP sync destination plugin.
485   */
486  @Override()
487  public final String toString()
488  {
489    final StringBuilder buffer = new StringBuilder();
490    toString(buffer);
491    return buffer.toString();
492  }
493
494
495
496  /**
497   * Appends a string representation of this LDAP sync destination plugin to the
498   * provided buffer.
499   *
500   * @param  buffer  The buffer to which the string representation should be
501   *                 appended.
502   */
503  public abstract void toString(final StringBuilder buffer);
504
505
506
507  /**
508   * {@inheritDoc}
509   */
510  public Map<List<String>,String> getExamplesArgumentSets()
511  {
512    return Collections.emptyMap();
513  }
514}