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