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 *      Portions Copyright 2010-2024 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.sync.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034import java.util.concurrent.atomic.AtomicReference;
035
036import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037import com.unboundid.directory.sdk.common.internal.Reconfigurable;
038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039import com.unboundid.directory.sdk.sync.config.LDAPSyncSourcePluginConfig;
040import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041import com.unboundid.directory.sdk.sync.types.PostStepResult;
042import com.unboundid.directory.sdk.sync.types.PreStepResult;
043import com.unboundid.directory.sdk.sync.types.SyncOperation;
044import com.unboundid.directory.sdk.sync.types.SyncServerContext;
045import com.unboundid.ldap.sdk.Entry;
046import com.unboundid.ldap.sdk.LDAPException;
047import com.unboundid.ldap.sdk.LDAPInterface;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.ldap.sdk.SearchRequest;
050import com.unboundid.ldap.sdk.SearchResultEntry;
051import com.unboundid.util.Extensible;
052import com.unboundid.util.ThreadSafety;
053import com.unboundid.util.ThreadSafetyLevel;
054import com.unboundid.util.args.ArgumentException;
055import com.unboundid.util.args.ArgumentParser;
056
057
058
059/**
060 * This class defines an API that must be implemented by extensions that
061 * perform processing on synchronization operations within an LDAP Sync
062 * Source.  These extensions may be used to
063 * <ul>
064 *   <li>Filter out certain changes from being synchronized.</li>
065 *   <li>Change how an entry is fetched.</li>
066 * </ul>
067 * <BR>
068 * A note on exception handling: in general subclasses should not
069 * catch LDAPExceptions that are thrown when using the provided
070 * LDAPInterface unless there are specific exceptions that are
071 * expected.  The Data Sync Server will handle
072 * LDAPExceptions in an appropriate way based on the specific
073 * cause of the exception.  For example, some errors will result
074 * in the SyncOperation being retried, and others will trigger
075 * fail over to a different server.
076 * <BR>
077 * <H2>Configuring LDAP Sync Source Plugins</H2>
078 * In order to configure an LDAP sync source plugin created using this API, use
079 * a command like:
080 * <PRE>
081 *      dsconfig create-sync-source-plugin \
082 *           --plugin-name "<I>{plugin-name}</I>" \
083 *           --type third-party-ldap \
084 *           --set "extension-class:<I>{class-name}</I>" \
085 *           --set "extension-argument:<I>{name=value}</I>"
086 * </PRE>
087 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync source
088 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the
089 * Java class that extends
090 * {@code com.unboundid.directory.sdk.sync.api.LDAPSyncSourcePlugin}, and
091 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
092 * provide to the LDAP sync source plugin.  If multiple arguments should be
093 * provided to the LDAP sync source plugin, then the
094 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
095 * provided multiple times.
096 *
097 * @see  com.unboundid.directory.sdk.sync.scripting.ScriptedLDAPSyncSourcePlugin
098 */
099@Extensible()
100@SynchronizationServerExtension(appliesToLocalContent=false,
101     appliesToSynchronizedContent=true)
102@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
103public abstract class LDAPSyncSourcePlugin
104       implements UnboundIDExtension,
105                  Reconfigurable<LDAPSyncSourcePluginConfig>,
106                  ExampleUsageProvider
107{
108  /**
109   * Creates a new instance of this LDAP sync source plugin.  All sync
110   * source implementations must include a default constructor, but any
111   * initialization should generally be done in the
112   * {@code initializeLDAPSyncSourcePlugin} method.
113   */
114  public LDAPSyncSourcePlugin()
115  {
116    // No implementation is required.
117  }
118
119
120
121  /**
122   * {@inheritDoc}
123   */
124  public abstract String getExtensionName();
125
126
127
128  /**
129   * {@inheritDoc}
130   */
131  public abstract String[] getExtensionDescription();
132
133
134
135  /**
136   * {@inheritDoc}
137   */
138  public void defineConfigArguments(final ArgumentParser parser)
139         throws ArgumentException
140  {
141    // No arguments will be allowed by default.
142  }
143
144
145
146  /**
147   * Initializes this LDAP sync source plugin.  This method will be called
148   * before any other methods in the class.
149   *
150   * @param  serverContext  A handle to the server context for the
151   *                        Data Sync Server in which this extension is
152   *                        running.  Extensions should typically store this
153   *                        in a class member.
154   * @param  config         The general configuration for this proxy
155   *                        transformation.
156   * @param  parser         The argument parser which has been initialized from
157   *                        the configuration for this LDAP sync source
158   *                        plugin.
159   *
160   * @throws  LDAPException  If a problem occurs while initializing this ldap
161   *                         sync source plugin.
162   */
163  public void initializeLDAPSyncSourcePlugin(
164                   final SyncServerContext serverContext,
165                   final LDAPSyncSourcePluginConfig config,
166                   final ArgumentParser parser)
167         throws LDAPException
168  {
169    // No initialization will be performed by default.
170  }
171
172
173
174  /**
175   * {@inheritDoc}
176   */
177  public boolean isConfigurationAcceptable(
178                      final LDAPSyncSourcePluginConfig config,
179                      final ArgumentParser parser,
180                      final List<String> unacceptableReasons)
181  {
182    // No extended validation will be performed by default.
183    return true;
184  }
185
186
187
188  /**
189   * {@inheritDoc}
190   */
191  public ResultCode applyConfiguration(final LDAPSyncSourcePluginConfig config,
192                                       final ArgumentParser parser,
193                                       final List<String> adminActionsRequired,
194                                       final List<String> messages)
195  {
196    // By default, no configuration changes will be applied.
197    return ResultCode.SUCCESS;
198  }
199
200
201
202  /**
203   * Performs any cleanup which may be necessary when this LDAP sync source
204   * plugin is taken out of service.  This can happen when it is deleted from
205   * the configuration and at server shutdown.
206   */
207  public void finalizeLDAPSyncSourcePlugin()
208  {
209    // No implementation is required.
210  }
211
212
213  /**
214   * This method is called before each search for a source entry. If this method
215   * should only be run once per operation then an attachment can be added
216   * to the SyncOp for tracking A connection to the source server is provided.
217   * This method is overridden by plugins that need to manipulate an operation
218   * prior to source entry being fetched.
219   *
220   * @param  sourceConnection       A connection to the source server.
221   * @param  searchRequest          The search request that the LDAP Sync
222   *                                Source will use to fetch the entry.
223   * @param  searchResults          A list of entries that have been fetched.
224   *                                When the search criteria matches multiple
225   *                                entries, they should all be returned.  A
226   *                                plugin that wishes to implement the fetch
227   *                                should put the fetched entries here and
228   *                                return
229   * @param  operation              The synchronization operation for this
230   *                                change.
231   *
232   * @return  The result of the plugin processing. Be very careful when
233   *          returning {@code PostStepResult#RETRY_OPERATION_UNLIMITED} as this
234   *          can stall all in flight operations until this operation completes.
235   *          This return value should only be used in situations where a
236   *          remote service (e.g., the LDAP server) is unavailable. In this
237   *          case, it's preferable to just throw the underlying LDAPException,
238   *          which the Data Sync Server will handle correctly based on
239   *          the type of the operation.
240   *
241   * @throws  LDAPException  In general subclasses should not catch
242   *                         LDAPExceptions that are thrown when
243   *                         using the LDAPInterface unless there
244   *                         are specific exceptions that are
245   *                         expected.  The Data Sync Server
246   *                         will handle LDAPExceptions in an
247   *                         appropriate way based on the specific
248   *                         cause of the exception.  For example,
249   *                         some errors will result in the
250   *                         SyncOperation being retried, and others
251   *                         will trigger fail over to a different
252   *                         server.  Plugins should only throw
253   *                         LDAPException for errors related to
254   *                         communication with the LDAP server.
255   *                         Use the return code to indicate other
256   *                         types of errors, which might require
257   *                         retry.
258   */
259  public PreStepResult preFetch(final LDAPInterface sourceConnection,
260                                final SearchRequest searchRequest,
261                                final List<SearchResultEntry> searchResults,
262                                final SyncOperation operation)
263    throws LDAPException
264  {
265    return PreStepResult.CONTINUE;
266  }
267
268
269  /**
270   * This method is called after fetching a source entry.  A
271   * connection to the source server is provided.  This method is
272   * overridden by plugins that need to manipulate the search results that
273   * are returned to the Sync Pipe.  This can include filtering out certain
274   * entries, remove information from the entries, or adding additional
275   * information, possibly by doing a followup LDAP search.
276   *
277   * @param  sourceConnection       A connection to the source server.
278   * @param  fetchedEntryRef        A reference to the entry that was fetched.
279   *                                This entry can be edited in place, or the
280   *                                reference can be changed to point to a
281   *                                different entry that the plugin constructs.
282   *                                The referenced entry might be {@code null}
283   *                                if no matched entry was found at the source.
284   * @param  operation              The synchronization operation for this
285   *                                change.
286   *
287   * @return  The result of the plugin processing. Be very careful when
288   *          returning {@code PostStepResult#RETRY_OPERATION_UNLIMITED} as this
289   *          can stall all in flight operations until this operation completes.
290   *          This return value should only be used in situations where a
291   *          remote service (e.g., the LDAP server) is unavailable. In this
292   *          case, it's preferable to just throw the underlying LDAPException,
293   *          which the Data Sync Server will handle correctly based on
294   *          the type of the operation.
295   *
296   * @throws  LDAPException  In general subclasses should not catch
297   *                         LDAPExceptions that are thrown when
298   *                         using the LDAPInterface unless there
299   *                         are specific exceptions that are
300   *                         expected.  The Data Sync Server
301   *                         will handle LDAPExceptions in an
302   *                         appropriate way based on the specific
303   *                         cause of the exception.  For example,
304   *                         some errors will result in the
305   *                         SyncOperation being retried, and others
306   *                         will trigger fail over to a different
307   *                         server.  Plugins should only throw
308   *                         LDAPException for errors related to
309   *                         communication with the LDAP server.
310   *                         Use the return code to indicate other
311   *                         types of errors, which might require
312   *                         retry.
313   */
314  public PostStepResult postFetch(final LDAPInterface sourceConnection,
315                                  final AtomicReference<Entry> fetchedEntryRef,
316                                  final SyncOperation operation)
317       throws LDAPException
318  {
319    return PostStepResult.CONTINUE;
320  }
321
322
323
324  /**
325   * Retrieves a string representation of this LDAP sync source plugin.
326   *
327   * @return  A string representation of this LDAP sync source plugin.
328   */
329  @Override()
330  public final String toString()
331  {
332    final StringBuilder buffer = new StringBuilder();
333    toString(buffer);
334    return buffer.toString();
335  }
336
337
338
339  /**
340   * Appends a string representation of this LDAP sync source plugin to the
341   * provided buffer.
342   *
343   * @param  buffer  The buffer to which the string representation should be
344   *                 appended.
345   */
346  public abstract void toString(final StringBuilder buffer);
347
348
349
350  /**
351   * {@inheritDoc}
352   */
353  public Map<List<String>,String> getExamplesArgumentSets()
354  {
355    return Collections.emptyMap();
356  }
357}