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