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-2014 UnboundID Corp.
026 */
027 package com.unboundid.directory.sdk.sync.scripting;
028
029
030
031 import java.util.List;
032
033 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034 import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig;
035 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
036 import com.unboundid.directory.sdk.sync.types.PostStepResult;
037 import com.unboundid.directory.sdk.sync.types.PreStepResult;
038 import com.unboundid.directory.sdk.sync.types.SyncOperation;
039 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
040 import com.unboundid.ldap.sdk.Entry;
041 import com.unboundid.ldap.sdk.LDAPException;
042 import com.unboundid.ldap.sdk.LDAPInterface;
043 import com.unboundid.ldap.sdk.Modification;
044 import com.unboundid.ldap.sdk.ResultCode;
045 import com.unboundid.ldap.sdk.SearchRequest;
046 import com.unboundid.util.Extensible;
047 import com.unboundid.util.ThreadSafety;
048 import com.unboundid.util.ThreadSafetyLevel;
049 import com.unboundid.util.args.ArgumentException;
050 import com.unboundid.util.args.ArgumentParser;
051
052
053
054 /**
055 * This class defines an API that must be implemented by scripted extensions
056 * that perform processing on synchronization operations within an LDAP Sync
057 * Destination. These extensions may be used to
058 * <ul>
059 * <li>Filter out certain changes from being synchronized.</li>
060 * <li>Change how an entry is fetched.</li>
061 * <li>Change how an entry is modified or created.</li>
062 * </ul>
063 * <BR>
064 * A note on exception handling: in general subclasses should not
065 * catch LDAPExceptions that are thrown when using the provided
066 * LDAPInterface unless there are specific exceptions that are
067 * expected. The Synchronization Server will handle
068 * LDAPExceptions in an appropriate way based on the specific
069 * cause of the exception. For example, some errors will result
070 * in the SyncOperation being retried, and others will trigger
071 * fail over to a different server.
072 * <BR>
073 * <H2>Configuring Groovy-Scripted LDAP Sync Destination Plugins</H2>
074 * In order to configure a scripted LDAP sync destination plugin based on this
075 * API and written in the Groovy scripting language, use a command like:
076 * <PRE>
077 * dsconfig create-sync-destination-plugin \
078 * --plugin-name "<I>{plugin-name}</I>" \
079 * --type groovy-scripted-ldap \
080 * --set "script-class:<I>{class-name}</I>" \
081 * --set "script-argument:<I>{name=value}</I>"
082 * </PRE>
083 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync destination
084 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the
085 * Groovy class written using this API, and "<I>{name=value}</I>" represents
086 * name-value pairs for any arguments to provide to the LDAP sync destination
087 * plugin. If multiple arguments should be provided to the LDAP sync
088 * destination plugin, then the
089 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
090 * provided multiple times.
091 *
092 * @see com.unboundid.directory.sdk.sync.api.LDAPSyncDestinationPlugin
093 */
094 @Extensible()
095 @SynchronizationServerExtension(appliesToLocalContent=false,
096 appliesToSynchronizedContent=true)
097 @ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
098 public abstract class ScriptedLDAPSyncDestinationPlugin
099 implements Reconfigurable<LDAPSyncDestinationPluginConfig>
100 {
101 /**
102 * Creates a new instance of this LDAP sync destination plugin. All sync
103 * destination implementations must include a default constructor, but any
104 * initialization should generally be done in the
105 * {@code initializeLDAPSyncDestinationPlugin} method.
106 */
107 public ScriptedLDAPSyncDestinationPlugin()
108 {
109 // No implementation is required.
110 }
111
112
113
114 /**
115 * {@inheritDoc}
116 */
117 public void defineConfigArguments(final ArgumentParser parser)
118 throws ArgumentException
119 {
120 // No arguments will be allowed by default.
121 }
122
123
124
125 /**
126 * Initializes this LDAP sync destination plugin.
127 *
128 * @param serverContext A handle to the server context for the server in
129 * which this extension is running.
130 * @param config The general configuration for this LDAP sync
131 * destination plugin transformation.
132 * @param parser The argument parser which has been initialized from
133 * the configuration for this LDAP sync destination
134 * plugin.
135 *
136 * @throws LDAPException If a problem occurs while initializing this LDAP
137 * sync destination plugin.
138 */
139 public void initializeLDAPSyncDestinationPlugin(
140 final SyncServerContext serverContext,
141 final LDAPSyncDestinationPluginConfig config,
142 final ArgumentParser parser)
143 throws LDAPException
144 {
145 // No initialization will be performed by default.
146 }
147
148
149
150 /**
151 * Performs any cleanup which may be necessary when this LDAP sync destination
152 * plugin is to be taken out of service.
153 */
154 public void finalizeLDAPSyncDestinationPlugin()
155 {
156 // No implementation is required.
157 }
158
159
160
161 /**
162 * {@inheritDoc}
163 */
164 public boolean isConfigurationAcceptable(
165 final LDAPSyncDestinationPluginConfig config,
166 final ArgumentParser parser,
167 final List<String> unacceptableReasons)
168 {
169 // No extended validation will be performed.
170 return true;
171 }
172
173
174
175 /**
176 * {@inheritDoc}
177 */
178 public ResultCode applyConfiguration(
179 final LDAPSyncDestinationPluginConfig config,
180 final ArgumentParser parser,
181 final List<String> adminActionsRequired,
182 final List<String> messages)
183 {
184 // By default, no configuration changes will be applied.
185 return ResultCode.SUCCESS;
186 }
187
188
189
190 /**
191 * This method is called before a destination entry is fetched. A
192 * connection to the destination server is provided along with the
193 * {@code SearchRequest} that will be sent to the server. This method is
194 * overridden by plugins that need to have access to the search request
195 * before it is sent to the destination server. This includes updating the
196 * search request as well as performing the search instead of the core server,
197 * including doing additional searches. For plugins that need to manipulate
198 * the entries that the core LDAP Sync Destination code retrieves from the
199 * destination, implementing the {@link #postFetch} method is more natural.
200 * <p>
201 * This method might be called multiple times for a single synchronization
202 * operation, specifically when there are multiple search criteria or
203 * multiple base DNs defined for the Sync Destination.
204 *
205 * @param destinationConnection A connection to the destination server.
206 * @param searchRequest The search request that the LDAP Sync
207 * Destination will use to fetch the entry.
208 * @param fetchedEntries A list of entries that have been fetched.
209 * When the search criteria matches multiple
210 * entries, they should all be returned. A
211 * plugin that wishes to implement the fetch
212 * should put the fetched entries here and
213 * return
214 * {@code PreStepResult#SKIP_CURRENT_STEP}.
215 * @param operation The synchronization operation for this
216 * change.
217 *
218 * @return The result of the plugin processing. Note:
219 * {@code PreStepResult#SKIP_CURRENT_STEP} should only be returned
220 * if this plugin takes responsibility for fully fetching the entry
221 * according to the search request and for populating the
222 * fetched entry list.
223 *
224 * @throws LDAPException In general subclasses should not catch
225 * LDAPExceptions that are thrown when
226 * using the LDAPInterface unless there
227 * are specific exceptions that are
228 * expected. The Synchronization Server
229 * will handle LDAPExceptions in an
230 * appropriate way based on the specific
231 * cause of the exception. For example,
232 * some errors will result in the
233 * SyncOperation being retried, and others
234 * will trigger fail over to a different
235 * server. Plugins should only throw
236 * LDAPException for errors related to
237 * communication with the LDAP server.
238 * Use the return code to indicate other
239 * types of errors, which might require
240 * retry.
241 */
242 public PreStepResult preFetch(final LDAPInterface destinationConnection,
243 final SearchRequest searchRequest,
244 final List<Entry> fetchedEntries,
245 final SyncOperation operation)
246 throws LDAPException
247 {
248 return PreStepResult.CONTINUE;
249 }
250
251
252
253 /**
254 * This method is called after an attempt to fetch a destination entry. An
255 * connection to the destination server is provided along with the
256 * {@code SearchRequest} that was sent to the server. This method is
257 * overridden by plugins that need to manipulate the search results that
258 * are returned to the Sync Pipe. This can include filtering out certain
259 * entries, remove information from the entries, or adding additional
260 * information, possibly by doing a followup LDAP search.
261 * <p>
262 * This method might be called multiple times for a single synchronization
263 * operation, specifically when there are multiple search criteria or
264 * multiple base DNs defined for the Sync Destination.
265 * <p>
266 * This method will not be called if the search fails, for instance, if
267 * the base DN of the search does not exist.
268 *
269 * @param destinationConnection A connection to the destination server.
270 * @param searchRequest The search request that the LDAP Sync
271 * Destination used to fetch the entry.
272 * @param fetchedEntries A list of entries that have been fetched.
273 * When the search criteria matches multiple
274 * entries, they will all be returned. Entries
275 * in this list can be edited directly, and the
276 * list can be edited as well.
277 * @param operation The synchronization operation for this
278 * change.
279 *
280 * @return The result of the plugin processing.
281 *
282 * @throws LDAPException In general subclasses should not catch
283 * LDAPExceptions that are thrown when
284 * using the LDAPInterface unless there
285 * are specific exceptions that are
286 * expected. The Synchronization Server
287 * will handle LDAPExceptions in an
288 * appropriate way based on the specific
289 * cause of the exception. For example,
290 * some errors will result in the
291 * SyncOperation being retried, and others
292 * will trigger fail over to a different
293 * server. Plugins should only throw
294 * LDAPException for errors related to
295 * communication with the LDAP server.
296 * Use the return code to indicate other
297 * types of errors, which might require
298 * retry.
299 */
300 public PostStepResult postFetch(final LDAPInterface destinationConnection,
301 final SearchRequest searchRequest,
302 final List<Entry> fetchedEntries,
303 final SyncOperation operation)
304 throws LDAPException
305 {
306 return PostStepResult.CONTINUE;
307 }
308
309
310
311 /**
312 * This method is called before a destination entry is created. A
313 * connection to the destination server is provided along with the
314 * {@code Entry} that will be sent to the server. This method is
315 * overridden by plugins that need to alter the entry before it is created
316 * at the server.
317 *
318 * @param destinationConnection A connection to the destination server.
319 * @param entryToCreate The entry that will be created at the
320 * destination. A plugin that wishes to
321 * create the entry should be sure to return
322 * {@code PreStepResult#SKIP_CURRENT_STEP}.
323 * @param operation The synchronization operation for this
324 * change.
325 *
326 * @return The result of the plugin processing.
327 *
328 * @throws LDAPException In general subclasses should not catch
329 * LDAPExceptions that are thrown when
330 * using the LDAPInterface unless there
331 * are specific exceptions that are
332 * expected. The Synchronization Server
333 * will handle LDAPExceptions in an
334 * appropriate way based on the specific
335 * cause of the exception. For example,
336 * some errors will result in the
337 * SyncOperation being retried, and others
338 * will trigger fail over to a different
339 * server. Plugins should only throw
340 * LDAPException for errors related to
341 * communication with the LDAP server.
342 * Use the return code to indicate other
343 * types of errors, which might require
344 * retry.
345 */
346 public PreStepResult preCreate(final LDAPInterface destinationConnection,
347 final Entry entryToCreate,
348 final SyncOperation operation)
349 throws LDAPException
350 {
351 return PreStepResult.CONTINUE;
352 }
353
354
355
356 /**
357 * This method is called before a destination entry is modified. A
358 * connection to the destination server is provided along with the
359 * {@code Entry} that will be sent to the server. This method is
360 * overridden by plugins that need to perform some processing on an entry
361 * before it is modified.
362 *
363 * @param destinationConnection A connection to the destination server.
364 * @param entryToModify The entry that will be modified at the
365 * destination. A plugin that wishes to
366 * modify the entry should be sure to return
367 * {@code PreStepResult#SKIP_CURRENT_STEP}.
368 * @param modsToApply A modifiable list of the modifications to
369 * apply at the server.
370 * @param operation The synchronization operation for this
371 * change.
372 *
373 * @return The result of the plugin processing.
374 *
375 * @throws LDAPException In general subclasses should not catch
376 * LDAPExceptions that are thrown when
377 * using the LDAPInterface unless there
378 * are specific exceptions that are
379 * expected. The Synchronization Server
380 * will handle LDAPExceptions in an
381 * appropriate way based on the specific
382 * cause of the exception. For example,
383 * some errors will result in the
384 * SyncOperation being retried, and others
385 * will trigger fail over to a different
386 * server. Plugins should only throw
387 * LDAPException for errors related to
388 * communication with the LDAP server.
389 * Use the return code to indicate other
390 * types of errors, which might require
391 * retry.
392 */
393 public PreStepResult preModify(final LDAPInterface destinationConnection,
394 final Entry entryToModify,
395 final List<Modification> modsToApply,
396 final SyncOperation operation)
397 throws LDAPException
398 {
399 return PreStepResult.CONTINUE;
400 }
401
402
403
404 /**
405 * This method is called before a destination entry is deleted. A
406 * connection to the destination server is provided along with the
407 * {@code Entry} that will be sent to the server. This method is
408 * overridden by plugins that need to perform some processing on an entry
409 * before it is deleted. A plugin could choose to mark an entry as disabled
410 * instead of deleting it for instance, or move the entry to a different
411 * part of the directory hierarchy.
412 *
413 * @param destinationConnection A connection to the destination server.
414 * @param entryToDelete The entry that will be deleted at the
415 * destination. A plugin that wishes to
416 * delete the entry should be sure to return
417 * {@code PreStepResult#SKIP_CURRENT_STEP}.
418 * @param operation The synchronization operation for this
419 * change.
420 *
421 * @return The result of the plugin processing.
422 *
423 * @throws LDAPException In general subclasses should not catch
424 * LDAPExceptions that are thrown when
425 * using the LDAPInterface unless there
426 * are specific exceptions that are
427 * expected. The Synchronization Server
428 * will handle LDAPExceptions in an
429 * appropriate way based on the specific
430 * cause of the exception. For example,
431 * some errors will result in the
432 * SyncOperation being retried, and others
433 * will trigger fail over to a different
434 * server. Plugins should only throw
435 * LDAPException for errors related to
436 * communication with the LDAP server.
437 * Use the return code to indicate other
438 * types of errors, which might require
439 * retry.
440 */
441 public PreStepResult preDelete(final LDAPInterface destinationConnection,
442 final Entry entryToDelete,
443 final SyncOperation operation)
444 throws LDAPException
445 {
446 return PreStepResult.CONTINUE;
447 }
448 }