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