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.scripting; 028 029 030 031import java.util.List; 032 033import com.unboundid.directory.sdk.common.internal.Reconfigurable; 034import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig; 035import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 036import com.unboundid.directory.sdk.sync.types.PostStepResult; 037import com.unboundid.directory.sdk.sync.types.PreStepResult; 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.Modification; 044import com.unboundid.ldap.sdk.ResultCode; 045import com.unboundid.ldap.sdk.SearchRequest; 046import com.unboundid.util.Extensible; 047import com.unboundid.util.ThreadSafety; 048import com.unboundid.util.ThreadSafetyLevel; 049import com.unboundid.util.args.ArgumentException; 050import 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) 098public 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}