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