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-2018 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.sync.scripting;
028
029
030
031import java.util.List;
032
033import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig;
035import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
036import com.unboundid.directory.sdk.sync.types.PostStepResult;
037import com.unboundid.directory.sdk.sync.types.PreStepResult;
038import com.unboundid.directory.sdk.sync.types.SyncOperation;
039import com.unboundid.directory.sdk.sync.types.SyncServerContext;
040import com.unboundid.ldap.sdk.Entry;
041import com.unboundid.ldap.sdk.LDAPException;
042import com.unboundid.ldap.sdk.LDAPInterface;
043import com.unboundid.ldap.sdk.Modification;
044import com.unboundid.ldap.sdk.ResultCode;
045import com.unboundid.ldap.sdk.SearchRequest;
046import com.unboundid.util.Extensible;
047import com.unboundid.util.ThreadSafety;
048import com.unboundid.util.ThreadSafetyLevel;
049import com.unboundid.util.args.ArgumentException;
050import com.unboundid.util.args.ArgumentParser;
051
052
053
054/**
055 * This class defines an API that must be implemented by scripted extensions
056 * that perform processing on synchronization operations within an LDAP Sync
057 * Destination.  These extensions may be used to
058 * <ul>
059 *   <li>Filter out certain changes from being synchronized.</li>
060 *   <li>Change how an entry is fetched.</li>
061 *   <li>Change how an entry is modified or created.</li>
062 * </ul>
063 * <BR>
064 * A note on exception handling: in general subclasses should not
065 * catch LDAPExceptions that are thrown when using the provided
066 * LDAPInterface unless there are specific exceptions that are
067 * expected.  The Synchronization Server will handle
068 * LDAPExceptions in an appropriate way based on the specific
069 * cause of the exception.  For example, some errors will result
070 * in the SyncOperation being retried, and others will trigger
071 * fail over to a different server.
072 * <BR>
073 * <H2>Configuring Groovy-Scripted LDAP Sync Destination Plugins</H2>
074 * In order to configure a scripted LDAP sync destination plugin based on this
075 * API and written in the Groovy scripting language, use a command like:
076 * <PRE>
077 *      dsconfig create-sync-destination-plugin \
078 *           --plugin-name "<I>{plugin-name}</I>" \
079 *           --type groovy-scripted-ldap \
080 *           --set "script-class:<I>{class-name}</I>" \
081 *           --set "script-argument:<I>{name=value}</I>"
082 * </PRE>
083 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync destination
084 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the
085 * Groovy class written using this API, and "<I>{name=value}</I>" represents
086 * name-value pairs for any arguments to provide to the LDAP sync destination
087 * plugin.  If multiple arguments should be provided to the LDAP sync
088 * destination plugin, then the
089 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
090 * provided multiple times.
091 *
092 * @see  com.unboundid.directory.sdk.sync.api.LDAPSyncDestinationPlugin
093 */
094@Extensible()
095@SynchronizationServerExtension(appliesToLocalContent=false,
096     appliesToSynchronizedContent=true)
097@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
098public abstract class ScriptedLDAPSyncDestinationPlugin
099       implements Reconfigurable<LDAPSyncDestinationPluginConfig>
100{
101  /**
102   * Creates a new instance of this LDAP sync destination plugin.  All sync
103   * destination implementations must include a default constructor, but any
104   * initialization should generally be done in the
105   * {@code initializeLDAPSyncDestinationPlugin} method.
106   */
107  public ScriptedLDAPSyncDestinationPlugin()
108  {
109    // No implementation is required.
110  }
111
112
113
114  /**
115   * {@inheritDoc}
116   */
117  public void defineConfigArguments(final ArgumentParser parser)
118         throws ArgumentException
119  {
120    // No arguments will be allowed by default.
121  }
122
123
124
125  /**
126   * Initializes this LDAP sync destination plugin.
127   *
128   * @param  serverContext  A handle to the server context for the server in
129   *                        which this extension is running.
130   * @param  config         The general configuration for this LDAP sync
131   *                        destination plugin transformation.
132   * @param  parser         The argument parser which has been initialized from
133   *                        the configuration for this LDAP sync destination
134   *                        plugin.
135   *
136   * @throws  LDAPException  If a problem occurs while initializing this LDAP
137   *                         sync destination plugin.
138   */
139  public void initializeLDAPSyncDestinationPlugin(
140                   final SyncServerContext serverContext,
141                   final LDAPSyncDestinationPluginConfig config,
142                   final ArgumentParser parser)
143         throws LDAPException
144  {
145    // No initialization will be performed by default.
146  }
147
148
149
150  /**
151   * Performs any cleanup which may be necessary when this LDAP sync destination
152   * plugin is to be taken out of service.
153   */
154  public void finalizeLDAPSyncDestinationPlugin()
155  {
156    // No implementation is required.
157  }
158
159
160
161  /**
162   * {@inheritDoc}
163   */
164  public boolean isConfigurationAcceptable(
165                      final LDAPSyncDestinationPluginConfig config,
166                      final ArgumentParser parser,
167                      final List<String> unacceptableReasons)
168  {
169    // No extended validation will be performed.
170    return true;
171  }
172
173
174
175  /**
176   * {@inheritDoc}
177   */
178  public ResultCode applyConfiguration(
179                         final LDAPSyncDestinationPluginConfig config,
180                         final ArgumentParser parser,
181                         final List<String> adminActionsRequired,
182                         final List<String> messages)
183  {
184    // By default, no configuration changes will be applied.
185    return ResultCode.SUCCESS;
186  }
187
188
189
190  /**
191   * This method is called before a destination entry is fetched.  A
192   * connection to the destination server is provided along with the
193   * {@code SearchRequest} that will be sent to the server.  This method is
194   * overridden by plugins that need to have access to the search request
195   * before it is sent to the destination server.  This includes updating the
196   * search request as well as performing the search instead of the core server,
197   * including doing additional searches.  For plugins that need to manipulate
198   * the entries that the core LDAP Sync Destination code retrieves from the
199   * destination, implementing the {@link #postFetch} method is more natural.
200   * <p>
201   * This method might be called multiple times for a single synchronization
202   * operation, specifically when there are multiple search criteria or
203   * multiple base DNs defined for the Sync Destination.
204   *
205   * @param  destinationConnection  A connection to the destination server.
206   * @param  searchRequest          The search request that the LDAP Sync
207   *                                Destination will use to fetch the entry.
208   * @param  fetchedEntries         A list of entries that have been fetched.
209   *                                When the search criteria matches multiple
210   *                                entries, they should all be returned.  A
211   *                                plugin that wishes to implement the fetch
212   *                                should put the fetched entries here and
213   *                                return
214   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
215   * @param  operation              The synchronization operation for this
216   *                                change.
217   *
218   * @return  The result of the plugin processing.  Note:
219   *          {@code PreStepResult#SKIP_CURRENT_STEP} should only be returned
220   *          if this plugin takes responsibility for fully fetching the entry
221   *          according to the search request and for populating the
222   *          fetched entry list.
223   *
224   * @throws  LDAPException  In general subclasses should not catch
225   *                         LDAPExceptions that are thrown when
226   *                         using the LDAPInterface unless there
227   *                         are specific exceptions that are
228   *                         expected.  The Synchronization Server
229   *                         will handle LDAPExceptions in an
230   *                         appropriate way based on the specific
231   *                         cause of the exception.  For example,
232   *                         some errors will result in the
233   *                         SyncOperation being retried, and others
234   *                         will trigger fail over to a different
235   *                         server.  Plugins should only throw
236   *                         LDAPException for errors related to
237   *                         communication with the LDAP server.
238   *                         Use the return code to indicate other
239   *                         types of errors, which might require
240   *                         retry.
241   */
242  public PreStepResult preFetch(final LDAPInterface destinationConnection,
243                                final SearchRequest searchRequest,
244                                final List<Entry> fetchedEntries,
245                                final SyncOperation operation)
246       throws LDAPException
247  {
248    return PreStepResult.CONTINUE;
249  }
250
251
252
253  /**
254   * This method is called after an attempt to fetch a destination entry.  An
255   * connection to the destination server is provided along with the
256   * {@code SearchRequest} that was sent to the server.  This method is
257   * overridden by plugins that need to manipulate the search results that
258   * are returned to the Sync Pipe.  This can include filtering out certain
259   * entries, remove information from the entries, or adding additional
260   * information, possibly by doing a followup LDAP search.
261   * <p>
262   * This method might be called multiple times for a single synchronization
263   * operation, specifically when there are multiple search criteria or
264   * multiple base DNs defined for the Sync Destination.
265   * <p>
266   * This method will not be called if the search fails, for instance, if
267   * the base DN of the search does not exist.
268   *
269   * @param  destinationConnection  A connection to the destination server.
270   * @param  searchRequest          The search request that the LDAP Sync
271   *                                Destination used to fetch the entry.
272   * @param  fetchedEntries         A list of entries that have been fetched.
273   *                                When the search criteria matches multiple
274   *                                entries, they will all be returned.  Entries
275   *                                in this list can be edited directly, and the
276   *                                list can be edited as well.
277   * @param  operation              The synchronization operation for this
278   *                                change.
279   *
280   * @return  The result of the plugin processing.
281   *
282   * @throws  LDAPException  In general subclasses should not catch
283   *                         LDAPExceptions that are thrown when
284   *                         using the LDAPInterface unless there
285   *                         are specific exceptions that are
286   *                         expected.  The Synchronization Server
287   *                         will handle LDAPExceptions in an
288   *                         appropriate way based on the specific
289   *                         cause of the exception.  For example,
290   *                         some errors will result in the
291   *                         SyncOperation being retried, and others
292   *                         will trigger fail over to a different
293   *                         server.  Plugins should only throw
294   *                         LDAPException for errors related to
295   *                         communication with the LDAP server.
296   *                         Use the return code to indicate other
297   *                         types of errors, which might require
298   *                         retry.
299   */
300  public PostStepResult postFetch(final LDAPInterface destinationConnection,
301                                  final SearchRequest searchRequest,
302                                  final List<Entry> fetchedEntries,
303                                  final SyncOperation operation)
304       throws LDAPException
305  {
306    return PostStepResult.CONTINUE;
307  }
308
309
310
311  /**
312   * This method is called before a destination entry is created.  A
313   * connection to the destination server is provided along with the
314   * {@code Entry} that will be sent to the server.  This method is
315   * overridden by plugins that need to alter the entry before it is created
316   * at the server.
317   *
318   * @param  destinationConnection  A connection to the destination server.
319   * @param  entryToCreate          The entry that will be created at the
320   *                                destination.  A plugin that wishes to
321   *                                create the entry should be sure to return
322   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
323   * @param  operation              The synchronization operation for this
324   *                                change.
325   *
326   * @return  The result of the plugin processing.
327   *
328   * @throws  LDAPException  In general subclasses should not catch
329   *                         LDAPExceptions that are thrown when
330   *                         using the LDAPInterface unless there
331   *                         are specific exceptions that are
332   *                         expected.  The Synchronization Server
333   *                         will handle LDAPExceptions in an
334   *                         appropriate way based on the specific
335   *                         cause of the exception.  For example,
336   *                         some errors will result in the
337   *                         SyncOperation being retried, and others
338   *                         will trigger fail over to a different
339   *                         server.  Plugins should only throw
340   *                         LDAPException for errors related to
341   *                         communication with the LDAP server.
342   *                         Use the return code to indicate other
343   *                         types of errors, which might require
344   *                         retry.
345   */
346  public PreStepResult preCreate(final LDAPInterface destinationConnection,
347                                 final Entry entryToCreate,
348                                 final SyncOperation operation)
349       throws LDAPException
350  {
351    return PreStepResult.CONTINUE;
352  }
353
354
355
356  /**
357   * This method is called before a destination entry is modified.  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 perform some processing on an entry
361   * before it is modified.
362   *
363   * @param  destinationConnection  A connection to the destination server.
364   * @param  entryToModify          The entry that will be modified at the
365   *                                destination.  A plugin that wishes to
366   *                                modify the entry should be sure to return
367   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
368   * @param  modsToApply            A modifiable list of the modifications to
369   *                                apply at the server.
370   * @param  operation              The synchronization operation for this
371   *                                change.
372   *
373   * @return  The result of the plugin processing.
374   *
375   * @throws  LDAPException  In general subclasses should not catch
376   *                         LDAPExceptions that are thrown when
377   *                         using the LDAPInterface unless there
378   *                         are specific exceptions that are
379   *                         expected.  The Synchronization Server
380   *                         will handle LDAPExceptions in an
381   *                         appropriate way based on the specific
382   *                         cause of the exception.  For example,
383   *                         some errors will result in the
384   *                         SyncOperation being retried, and others
385   *                         will trigger fail over to a different
386   *                         server.  Plugins should only throw
387   *                         LDAPException for errors related to
388   *                         communication with the LDAP server.
389   *                         Use the return code to indicate other
390   *                         types of errors, which might require
391   *                         retry.
392   */
393  public PreStepResult preModify(final LDAPInterface destinationConnection,
394                                 final Entry entryToModify,
395                                 final List<Modification> modsToApply,
396                                 final SyncOperation operation)
397       throws LDAPException
398  {
399    return PreStepResult.CONTINUE;
400  }
401
402
403
404  /**
405   * This method is called before a destination entry is deleted.  A
406   * connection to the destination server is provided along with the
407   * {@code Entry} that will be sent to the server.  This method is
408   * overridden by plugins that need to perform some processing on an entry
409   * before it is deleted.  A plugin could choose to mark an entry as disabled
410   * instead of deleting it for instance, or move the entry to a different
411   * part of the directory hierarchy.
412   *
413   * @param  destinationConnection  A connection to the destination server.
414   * @param  entryToDelete          The entry that will be deleted at the
415   *                                destination.  A plugin that wishes to
416   *                                delete the entry should be sure to return
417   *                                {@code PreStepResult#SKIP_CURRENT_STEP}.
418   * @param  operation              The synchronization operation for this
419   *                                change.
420   *
421   * @return  The result of the plugin processing.
422   *
423   * @throws  LDAPException  In general subclasses should not catch
424   *                         LDAPExceptions that are thrown when
425   *                         using the LDAPInterface unless there
426   *                         are specific exceptions that are
427   *                         expected.  The Synchronization Server
428   *                         will handle LDAPExceptions in an
429   *                         appropriate way based on the specific
430   *                         cause of the exception.  For example,
431   *                         some errors will result in the
432   *                         SyncOperation being retried, and others
433   *                         will trigger fail over to a different
434   *                         server.  Plugins should only throw
435   *                         LDAPException for errors related to
436   *                         communication with the LDAP server.
437   *                         Use the return code to indicate other
438   *                         types of errors, which might require
439   *                         retry.
440   */
441  public PreStepResult preDelete(final LDAPInterface destinationConnection,
442                                 final Entry entryToDelete,
443                                 final SyncOperation operation)
444       throws LDAPException
445  {
446    return PreStepResult.CONTINUE;
447  }
448}