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-2023 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.sync.scripting;
028
029
030
031import java.util.List;
032import java.util.concurrent.atomic.AtomicReference;
033
034import com.unboundid.directory.sdk.common.internal.Reconfigurable;
035import com.unboundid.directory.sdk.sync.config.LDAPSyncSourcePluginConfig;
036import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
037import com.unboundid.directory.sdk.sync.types.PostStepResult;
038import com.unboundid.directory.sdk.sync.types.SyncOperation;
039import com.unboundid.directory.sdk.sync.types.SyncServerContext;
040import com.unboundid.ldap.sdk.Entry;
041import com.unboundid.ldap.sdk.LDAPException;
042import com.unboundid.ldap.sdk.LDAPInterface;
043import com.unboundid.ldap.sdk.ResultCode;
044import com.unboundid.util.Extensible;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047import com.unboundid.util.args.ArgumentException;
048import com.unboundid.util.args.ArgumentParser;
049
050
051
052/**
053 * This class defines an API that must be implemented by scripted extensions
054 * that perform processing on synchronization operations within an LDAP Sync
055 * Source.  These extensions may be used to
056 * <ul>
057 *   <li>Filter out certain changes from being synchronized.</li>
058 *   <li>Change how an entry is fetched.</li>
059 * </ul>
060 * <BR>
061 * A note on exception handling: in general subclasses should not
062 * catch LDAPExceptions that are thrown when using the provided
063 * LDAPInterface unless there are specific exceptions that are
064 * expected.  The Data Sync Server will handle
065 * LDAPExceptions in an appropriate way based on the specific
066 * cause of the exception.  For example, some errors will result
067 * in the SyncOperation being retried, and others will trigger
068 * fail over to a different server.
069 * <BR>
070 * <H2>Configuring Groovy-Scripted LDAP Sync Source Plugins</H2>
071 * In order to configure a scripted LDAP sync source plugin based on this API
072 * and written in the Groovy scripting language, use a command like:
073 * <PRE>
074 *      dsconfig create-sync-source-plugin \
075 *           --plugin-name "<I>{plugin-name}</I>" \
076 *           --type groovy-scripted-ldap \
077 *           --set "script-class:<I>{class-name}</I>" \
078 *           --set "script-argument:<I>{name=value}</I>"
079 * </PRE>
080 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync source
081 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the
082 * Groovy class written using this API, and "<I>{name=value}</I>" represents
083 * name-value pairs for any arguments to provide to the LDAP sync source plugin.
084 * If multiple arguments should be provided to the LDAP sync source plugin, then
085 * the "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
086 * provided multiple times.
087 *
088 * @see  com.unboundid.directory.sdk.sync.api.LDAPSyncSourcePlugin
089 */
090@Extensible()
091@SynchronizationServerExtension(appliesToLocalContent=false,
092     appliesToSynchronizedContent=true)
093@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
094public abstract class ScriptedLDAPSyncSourcePlugin
095       implements Reconfigurable<LDAPSyncSourcePluginConfig>
096{
097  /**
098   * Creates a new instance of this LDAP sync source plugin.  All sync
099   * source implementations must include a default constructor, but any
100   * initialization should generally be done in the
101   * {@code initializeLDAPSyncSourcePlugin} method.
102   */
103  public ScriptedLDAPSyncSourcePlugin()
104  {
105    // No implementation is required.
106  }
107
108
109
110  /**
111   * {@inheritDoc}
112   */
113  public void defineConfigArguments(final ArgumentParser parser)
114         throws ArgumentException
115  {
116    // No arguments will be allowed by default.
117  }
118
119
120
121  /**
122   * Initializes this LDAP sync source plugin.
123   *
124   * @param  serverContext  A handle to the server context for the server in
125   *                        which this extension is running.
126   * @param  config         The general configuration for this LDAP sync
127   *                        source plugin transformation.
128   * @param  parser         The argument parser which has been initialized from
129   *                        the configuration for this LDAP sync source plugin.
130   *
131   * @throws  LDAPException  If a problem occurs while initializing this LDAP
132   *                         sync source plugin.
133   */
134  public void initializeLDAPSyncSourcePlugin(
135                   final SyncServerContext serverContext,
136                   final LDAPSyncSourcePluginConfig config,
137                   final ArgumentParser parser)
138         throws LDAPException
139  {
140    // No initialization will be performed by default.
141  }
142
143
144
145  /**
146   * Performs any cleanup which may be necessary when this LDAP sync source
147   * plugin is to be taken out of service.
148   */
149  public void finalizeLDAPSyncSourcePlugin()
150  {
151    // No implementation is required.
152  }
153
154
155
156  /**
157   * {@inheritDoc}
158   */
159  public boolean isConfigurationAcceptable(
160                      final LDAPSyncSourcePluginConfig config,
161                      final ArgumentParser parser,
162                      final List<String> unacceptableReasons)
163  {
164    // No extended validation will be performed.
165    return true;
166  }
167
168
169
170  /**
171   * {@inheritDoc}
172   */
173  public ResultCode applyConfiguration(final LDAPSyncSourcePluginConfig config,
174                         final ArgumentParser parser,
175                         final List<String> adminActionsRequired,
176                         final List<String> messages)
177  {
178    // By default, no configuration changes will be applied.
179    return ResultCode.SUCCESS;
180  }
181
182
183
184  /**
185   * This method is called after fetching a source entry.  A
186   * connection to the source server is provided.  This method is
187   * overridden by plugins that need to manipulate the search results that
188   * are returned to the Sync Pipe.  This can include filtering out certain
189   * entries, remove information from the entries, or adding additional
190   * information, possibly by doing a followup LDAP search.
191   *
192   * @param  sourceConnection       A connection to the source server.
193   * @param  fetchedEntryRef        A reference to the entry that was fetched.
194   *                                This entry can be edited in place, or the
195   *                                reference can be changed to point to a
196   *                                different entry that the plugin constructs.
197   * @param  operation              The synchronization operation for this
198   *                                change.
199   *
200   * @return  The result of the plugin processing.
201   *
202   * @throws  LDAPException  In general subclasses should not catch
203   *                         LDAPExceptions that are thrown when
204   *                         using the LDAPInterface unless there
205   *                         are specific exceptions that are
206   *                         expected.  The Data Sync Server
207   *                         will handle LDAPExceptions in an
208   *                         appropriate way based on the specific
209   *                         cause of the exception.  For example,
210   *                         some errors will result in the
211   *                         SyncOperation being retried, and others
212   *                         will trigger fail over to a different
213   *                         server.  Plugins should only throw
214   *                         LDAPException for errors related to
215   *                         communication with the LDAP server.
216   *                         Use the return code to indicate other
217   *                         types of errors, which might require
218   *                         retry.
219   */
220  public PostStepResult postFetch(final LDAPInterface sourceConnection,
221                                  final AtomicReference<Entry> fetchedEntryRef,
222                                  final SyncOperation operation)
223       throws LDAPException
224  {
225    return PostStepResult.CONTINUE;
226  }
227}