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;
034
035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.directory.sdk.sync.config.SyncPipePluginConfig;
039import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
040import com.unboundid.directory.sdk.sync.types.PostStepResult;
041import com.unboundid.directory.sdk.sync.types.PreStepResult;
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.ResultCode;
047import com.unboundid.util.Extensible;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.args.ArgumentException;
051import com.unboundid.util.args.ArgumentParser;
052
053
054
055/**
056 * This class defines an API that must be implemented by extensions that
057 * perform processing on synchronization operations within the Sync Pipe.  These
058 * extensions may be used to
059 * <ul>
060 *   <li>Filter out certain changes from being synchronized.</li>
061 *   <li>Add and remove attributes that should be synchronized with the
062 *       destination independent of whether they changed at the source or
063 *       not.</li>
064 *   <li>Manipulate the changes that are synchronized to ignore certain
065 *       modified attributes or change the representation of modified
066 *       attributes.</li>
067 *   <li>Skip certain steps in Sync Pipe processing, e.g. attribute
068 *       and DN mapping.</li>
069 * </ul>
070 * Most plugins will need to override the {@code postMapping} method but not
071 * the {@code preMapping} method.  These extensions do not have access to the
072 * Sync Source or Sync Destination.
073 * <BR>
074 * <H2>Configuring Sync Pipe Plugins</H2>
075 * In order to configure a sync pipe plugin created using this API, use a
076 * command like:
077 * <PRE>
078 *      dsconfig create-sync-pipe-plugin \
079 *           --plugin-name "<I>{plugin-name}</I>" \
080 *           --type third-party \
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 sync pipe plugin
085 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
086 * that extends {@code com.unboundid.directory.sdk.sync.api.SyncPipePlugin},
087 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
088 * provide to the sync pipe plugin.  If multiple arguments should be provided to
089 * the sync pipe plugin, then the
090 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
091 * provided multiple times.
092 *
093 * @see  com.unboundid.directory.sdk.sync.scripting.ScriptedSyncPipePlugin
094 */
095@Extensible()
096@SynchronizationServerExtension(appliesToLocalContent=false,
097     appliesToSynchronizedContent=true)
098@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
099public abstract class SyncPipePlugin
100       implements UnboundIDExtension, Reconfigurable<SyncPipePluginConfig>,
101        ExampleUsageProvider
102{
103  /**
104   * Creates a new instance of this sync pipe plugin.  All sync pipe plugin
105   * implementations must include a default constructor, but any
106   * initialization should generally be done in the
107   * {@code initializeSyncPipePlugin} method.
108   */
109  public SyncPipePlugin()
110  {
111    // No implementation is required.
112  }
113
114
115
116  /**
117   * {@inheritDoc}
118   */
119  public abstract String getExtensionName();
120
121
122
123  /**
124   * {@inheritDoc}
125   */
126  public abstract String[] getExtensionDescription();
127
128
129
130  /**
131   * {@inheritDoc}
132   */
133  public void defineConfigArguments(final ArgumentParser parser)
134         throws ArgumentException
135  {
136    // No arguments will be allowed by default.
137  }
138
139
140
141  /**
142   * Initializes this sync pipe plugin.  This method will be called before
143   * any other methods in the class.
144   *
145   * @param  serverContext  A handle to the server context for the
146   *                        Data Sync Server in which this extension is
147   *                        running.  Extensions should typically store this
148   *                        in a class member.
149   * @param  config         The general configuration for this proxy
150   *                        transformation.
151   * @param  parser         The argument parser which has been initialized from
152   *                        the configuration for this sync pipe plugin.
153   *
154   * @throws  LDAPException  If a problem occurs while initializing this sync
155   *                         pipe plugin.
156   */
157  public void initializeSyncPipePlugin(final SyncServerContext serverContext,
158                                       final SyncPipePluginConfig config,
159                                       final ArgumentParser parser)
160         throws LDAPException
161  {
162    // No initialization will be performed by default.
163  }
164
165
166
167  /**
168   * {@inheritDoc}
169   */
170  public boolean isConfigurationAcceptable(
171                      final SyncPipePluginConfig config,
172                      final ArgumentParser parser,
173                      final List<String> unacceptableReasons)
174  {
175    // No extended validation will be performed by default.
176    return true;
177  }
178
179
180
181  /**
182   * {@inheritDoc}
183   */
184  public ResultCode applyConfiguration(final SyncPipePluginConfig config,
185                                       final ArgumentParser parser,
186                                       final List<String> adminActionsRequired,
187                                       final List<String> messages)
188  {
189    // By default, no configuration changes will be applied.
190    return ResultCode.SUCCESS;
191  }
192
193
194
195  /**
196   * Performs any cleanup which may be necessary when this sync pipe plugin
197   * is taken out of service.  This can happen when it is deleted from the
198   * configuration and at server shutdown.
199   */
200  public void finalizeSyncPipePlugin()
201  {
202    // No implementation is required.
203  }
204
205
206
207  /**
208   * This method is called immediately before the attributes and DN in
209   * the source entry are mapped into the equivalent destination entry.
210   * This equivalent destination entry is then compared to the actual
211   * destination entry to determine which of the modified attributes need
212   * to be updated.
213   * <p>
214   * This method is typically used to either filter out certain changes
215   * (by returning {@link PreStepResult#ABORT_OPERATION}) or to manipulate the
216   * source entry before it is converted into an equivalent destination entry.
217   * Attributes that will not otherwise be affected by attribute mapping
218   * can be set in {@code equivalentDestinationEntry}.  Although, due to the
219   * possibility of being overwritten in the mapping phase, manipulation of
220   * {@code equivalentDestinationEntry} is typically reserved for the
221   * {@link #postMapping} method.
222   * <p>
223   * The set of source attributes that should be synchronized at the destination
224   * can be manipulated by calling
225   * {@link SyncOperation#addModifiedSourceAttribute} and
226   * {@link SyncOperation#removeModifiedSourceAttribute} on
227   * {@code operation}.
228   * <p>
229   * Additional steps must be taken if this plugin adds destination attributes
230   * in {@code equivalentDestinationEntry} that need to be modified at the
231   * destination.  For operations with an operation type of
232   * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#CREATE},
233   * any updates made to
234   * {@code equivalentDestinationEntry} will be included in the
235   * entry created at the destination.  However, for operations with an
236   * operation type of
237   * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#MODIFY},
238   * destination attributes
239   * added by this plugin that need to be modified must be updated
240   * explicitly by calling
241   * {@link SyncOperation#addModifiedDestinationAttribute}.
242   * <p>
243   * With the exception of aborting changes or skipping the mapping step
244   * completely, most plugins will not need to override this method since
245   * the {@link #postMapping} method has access to the fully mapped destination
246   * entry.
247   *
248   * @param  sourceEntry                 The entry that was fetched from the
249   *                                     source.
250   * @param  equivalentDestinationEntry  The destination entry that is
251   *                                     equivalent to the source.  This entry
252   *                                     will be empty except for any
253   *                                     modifications that have been performed
254   *                                     by other sync pipe plugins.
255   * @param  operation                   The operation that is being
256   *                                     synchronized.
257   *
258   * @return  The result of the plugin processing.  Note:
259   *          {@code PreStepResult#3SKIP_CURRENT_STEP} should only be returned
260   *          if this plugin takes responsibility for fully constructing the
261   *          equivalent destination entry.
262   */
263  public PreStepResult preMapping(final Entry sourceEntry,
264                                  final Entry equivalentDestinationEntry,
265                                  final SyncOperation operation)
266  {
267    return PreStepResult.CONTINUE;
268  }
269
270
271
272  /**
273   * This method is called immediately after the attributes and DN in
274   * the source entry are mapped into the equivalent destination entry.
275   * Once this mapping is complete, this equivalent destination entry is then
276   * compared to the actual destination entry to determine which of the modified
277   * attributes need to be updated.
278   * <p>
279   * This method is typically used to manipulate the equivalent destination
280   * entry before these necessary changes are calculated.  It can also be used
281   * to filter out certain changes (by returning
282   * {@link PostStepResult#ABORT_OPERATION}), but this is typically done in
283   * the {@link #preMapping} method.
284   * <p>
285   * The set of source attributes that should be synchronized at the destination
286   * can be manipulated by calling
287   * {@link SyncOperation#addModifiedSourceAttribute} and
288   * {@link SyncOperation#removeModifiedSourceAttribute} on
289   * {@code operation}.
290   * <p>
291   * Additional steps must be taken if this plugin adds destination attributes
292   * in {@code equivalentDestinationEntry} that need to be modified at the
293   * destination.  For operations with an operation type of
294   * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#CREATE},
295   * any updates made to
296   * {@code equivalentDestinationEntry} will be included in the
297   * entry created at the destination.  However, for operations with an
298   * operation type of
299   * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#MODIFY},
300   * destination attributes
301   * added by this plugin that need to be modified must be updated
302   * explicitly by calling
303   * {@link SyncOperation#addModifiedDestinationAttribute}.
304   * <p>
305   * With the exception of aborting changes or skipping the mapping step
306   * completely, most plugins will override this method instead of
307   * {@link #preMapping} because this method has access to the fully mapped
308   * destination entry.
309   *
310   * @param  sourceEntry                 The entry that was fetched from the
311   *                                     source.
312   * @param  equivalentDestinationEntry  The destination entry that is
313   *                                     equivalent to the source.  This entry
314   *                                     will include all attributes mapped
315   *                                     from the source entry.
316   * @param  operation                   The operation that is being
317   *                                     synchronized.
318   *
319   * @return  The result of the plugin processing.
320   */
321  public PostStepResult postMapping(final Entry sourceEntry,
322                                    final Entry equivalentDestinationEntry,
323                                    final SyncOperation operation)
324  {
325    return PostStepResult.CONTINUE;
326  }
327
328
329
330  /**
331   * Retrieves a string representation of this sync pipe plugin.
332   *
333   * @return  A string representation of this sync pipe plugin.
334   */
335  @Override()
336  public final String toString()
337  {
338    final StringBuilder buffer = new StringBuilder();
339    toString(buffer);
340    return buffer.toString();
341  }
342
343
344
345  /**
346   * Appends a string representation of this sync pipe plugin to the provided
347   * buffer.
348   *
349   * @param  buffer  The buffer to which the string representation should be
350   *                 appended.
351   */
352  public abstract void toString(final StringBuilder buffer);
353
354
355
356  /**
357   * {@inheritDoc}
358   */
359  public Map<List<String>,String> getExamplesArgumentSets()
360  {
361    return Collections.emptyMap();
362  }
363}