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-2013 UnboundID Corp.
026     */
027    package com.unboundid.directory.sdk.sync.scripting;
028    
029    
030    
031    import java.util.List;
032    
033    import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034    import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig;
035    import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
036    import com.unboundid.directory.sdk.sync.types.PostStepResult;
037    import com.unboundid.directory.sdk.sync.types.PreStepResult;
038    import com.unboundid.directory.sdk.sync.types.SyncOperation;
039    import com.unboundid.directory.sdk.sync.types.SyncServerContext;
040    import com.unboundid.ldap.sdk.Entry;
041    import com.unboundid.ldap.sdk.LDAPException;
042    import com.unboundid.ldap.sdk.LDAPInterface;
043    import com.unboundid.ldap.sdk.Modification;
044    import com.unboundid.ldap.sdk.ResultCode;
045    import com.unboundid.ldap.sdk.SearchRequest;
046    import com.unboundid.util.Extensible;
047    import com.unboundid.util.ThreadSafety;
048    import com.unboundid.util.ThreadSafetyLevel;
049    import com.unboundid.util.args.ArgumentException;
050    import 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)
098    public 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    }