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.api; 028 029 030 031 import java.util.Collections; 032 import java.util.List; 033 import java.util.Map; 034 035 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 036 import com.unboundid.directory.sdk.common.internal.Reconfigurable; 037 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 038 import com.unboundid.directory.sdk.sync.config.LDAPSyncDestinationPluginConfig; 039 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 040 import com.unboundid.directory.sdk.sync.scripting. 041 ScriptedLDAPSyncDestinationPlugin; 042 import com.unboundid.directory.sdk.sync.types.PostStepResult; 043 import com.unboundid.directory.sdk.sync.types.PreStepResult; 044 import com.unboundid.directory.sdk.sync.types.SyncOperation; 045 import com.unboundid.directory.sdk.sync.types.SyncServerContext; 046 import com.unboundid.ldap.sdk.Entry; 047 import com.unboundid.ldap.sdk.LDAPException; 048 import com.unboundid.ldap.sdk.LDAPInterface; 049 import com.unboundid.ldap.sdk.Modification; 050 import com.unboundid.ldap.sdk.ResultCode; 051 import com.unboundid.ldap.sdk.SearchRequest; 052 import com.unboundid.util.Extensible; 053 import com.unboundid.util.ThreadSafety; 054 import com.unboundid.util.ThreadSafetyLevel; 055 import com.unboundid.util.args.ArgumentException; 056 import 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) 105 public 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. Note: 249 * {@code PreStepResult#SKIP_CURRENT_STEP} should only be returned 250 * if this plugin takes responsibility for fully fetching the entry 251 * according to the search request and for populating the 252 * fetched entry list. 253 * 254 * @throws LDAPException In general subclasses should not catch 255 * LDAPExceptions that are thrown when 256 * using the LDAPInterface unless there 257 * are specific exceptions that are 258 * expected. The Synchronization Server 259 * will handle LDAPExceptions in an 260 * appropriate way based on the specific 261 * cause of the exception. For example, 262 * some errors will result in the 263 * SyncOperation being retried, and others 264 * will trigger fail over to a different 265 * server. Plugins should only throw 266 * LDAPException for errors related to 267 * communication with the LDAP server. 268 * Use the return code to indicate other 269 * types of errors, which might require 270 * retry. 271 */ 272 public PreStepResult preFetch(final LDAPInterface destinationConnection, 273 final SearchRequest searchRequest, 274 final List<Entry> fetchedEntries, 275 final SyncOperation operation) 276 throws LDAPException 277 { 278 return PreStepResult.CONTINUE; 279 } 280 281 282 283 /** 284 * This method is called after an attempt to fetch a destination entry. An 285 * connection to the destination server is provided along with the 286 * {@code SearchRequest} that was sent to the server. This method is 287 * overridden by plugins that need to manipulate the search results that 288 * are returned to the Sync Pipe. This can include filtering out certain 289 * entries, remove information from the entries, or adding additional 290 * information, possibly by doing a followup LDAP search. 291 * <p> 292 * This method might be called multiple times for a single synchronization 293 * operation, specifically when there are multiple search criteria or 294 * multiple base DNs defined for the Sync Destination. 295 * <p> 296 * This method will not be called if the search fails, for instance, if 297 * the base DN of the search does not exist. 298 * 299 * @param destinationConnection A connection to the destination server. 300 * @param searchRequest The search request that the LDAP Sync 301 * Destination used to fetch the entry. 302 * @param fetchedEntries A list of entries that have been fetched. 303 * When the search criteria matches multiple 304 * entries, they will all be returned. Entries 305 * in this list can be edited directly, and the 306 * list can be edited as well. 307 * @param operation The synchronization operation for this 308 * change. 309 * 310 * @return The result of the plugin processing. 311 * 312 * @throws LDAPException In general subclasses should not catch 313 * LDAPExceptions that are thrown when 314 * using the LDAPInterface unless there 315 * are specific exceptions that are 316 * expected. The Synchronization Server 317 * will handle LDAPExceptions in an 318 * appropriate way based on the specific 319 * cause of the exception. For example, 320 * some errors will result in the 321 * SyncOperation being retried, and others 322 * will trigger fail over to a different 323 * server. Plugins should only throw 324 * LDAPException for errors related to 325 * communication with the LDAP server. 326 * Use the return code to indicate other 327 * types of errors, which might require 328 * retry. 329 */ 330 public PostStepResult postFetch(final LDAPInterface destinationConnection, 331 final SearchRequest searchRequest, 332 final List<Entry> fetchedEntries, 333 final SyncOperation operation) 334 throws LDAPException 335 { 336 return PostStepResult.CONTINUE; 337 } 338 339 340 341 /** 342 * This method is called before a destination entry is created. A 343 * connection to the destination server is provided along with the 344 * {@code Entry} that will be sent to the server. This method is 345 * overridden by plugins that need to alter the entry before it is created 346 * at the server. 347 * 348 * @param destinationConnection A connection to the destination server. 349 * @param entryToCreate The entry that will be created at the 350 * destination. A plugin that wishes to 351 * create the entry should be sure to return 352 * {@code PreStepResult#SKIP_CURRENT_STEP}. 353 * @param operation The synchronization operation for this 354 * change. 355 * 356 * @return The result of the plugin processing. 357 * 358 * @throws LDAPException In general subclasses should not catch 359 * LDAPExceptions that are thrown when 360 * using the LDAPInterface unless there 361 * are specific exceptions that are 362 * expected. The Synchronization Server 363 * will handle LDAPExceptions in an 364 * appropriate way based on the specific 365 * cause of the exception. For example, 366 * some errors will result in the 367 * SyncOperation being retried, and others 368 * will trigger fail over to a different 369 * server. Plugins should only throw 370 * LDAPException for errors related to 371 * communication with the LDAP server. 372 * Use the return code to indicate other 373 * types of errors, which might require 374 * retry. 375 */ 376 public PreStepResult preCreate(final LDAPInterface destinationConnection, 377 final Entry entryToCreate, 378 final SyncOperation operation) 379 throws LDAPException 380 { 381 return PreStepResult.CONTINUE; 382 } 383 384 385 386 /** 387 * This method is called before a destination entry is modified. A 388 * connection to the destination server is provided along with the 389 * {@code Entry} that will be sent to the server. This method is 390 * overridden by plugins that need to perform some processing on an entry 391 * before it is modified. 392 * 393 * @param destinationConnection A connection to the destination server. 394 * @param entryToModify The entry that will be modified at the 395 * destination. A plugin that wishes to 396 * modify the entry should be sure to return 397 * {@code PreStepResult#SKIP_CURRENT_STEP}. 398 * @param modsToApply A modifiable list of the modifications to 399 * apply at the server. 400 * @param operation The synchronization operation for this 401 * change. 402 * 403 * @return The result of the plugin processing. 404 * 405 * @throws LDAPException In general subclasses should not catch 406 * LDAPExceptions that are thrown when 407 * using the LDAPInterface unless there 408 * are specific exceptions that are 409 * expected. The Synchronization Server 410 * will handle LDAPExceptions in an 411 * appropriate way based on the specific 412 * cause of the exception. For example, 413 * some errors will result in the 414 * SyncOperation being retried, and others 415 * will trigger fail over to a different 416 * server. Plugins should only throw 417 * LDAPException for errors related to 418 * communication with the LDAP server. 419 * Use the return code to indicate other 420 * types of errors, which might require 421 * retry. 422 */ 423 public PreStepResult preModify(final LDAPInterface destinationConnection, 424 final Entry entryToModify, 425 final List<Modification> modsToApply, 426 final SyncOperation operation) 427 throws LDAPException 428 { 429 return PreStepResult.CONTINUE; 430 } 431 432 433 434 /** 435 * This method is called before a destination entry is deleted. A 436 * connection to the destination server is provided along with the 437 * {@code Entry} that will be sent to the server. This method is 438 * overridden by plugins that need to perform some processing on an entry 439 * before it is deleted. A plugin could choose to mark an entry as disabled 440 * instead of deleting it for instance, or move the entry to a different 441 * part of the directory hierarchy. 442 * 443 * @param destinationConnection A connection to the destination server. 444 * @param entryToDelete The entry that will be deleted at the 445 * destination. A plugin that wishes to 446 * delete the entry should be sure to return 447 * {@code PreStepResult#SKIP_CURRENT_STEP}. 448 * @param operation The synchronization operation for this 449 * change. 450 * 451 * @return The result of the plugin processing. 452 * 453 * @throws LDAPException In general subclasses should not catch 454 * LDAPExceptions that are thrown when 455 * using the LDAPInterface unless there 456 * are specific exceptions that are 457 * expected. The Synchronization Server 458 * will handle LDAPExceptions in an 459 * appropriate way based on the specific 460 * cause of the exception. For example, 461 * some errors will result in the 462 * SyncOperation being retried, and others 463 * will trigger fail over to a different 464 * server. Plugins should only throw 465 * LDAPException for errors related to 466 * communication with the LDAP server. 467 * Use the return code to indicate other 468 * types of errors, which might require 469 * retry. 470 */ 471 public PreStepResult preDelete(final LDAPInterface destinationConnection, 472 final Entry entryToDelete, 473 final SyncOperation operation) 474 throws LDAPException 475 { 476 return PreStepResult.CONTINUE; 477 } 478 479 480 481 /** 482 * Retrieves a string representation of this LDAP sync destination plugin. 483 * 484 * @return A string representation of this LDAP sync destination plugin. 485 */ 486 @Override() 487 public final String toString() 488 { 489 final StringBuilder buffer = new StringBuilder(); 490 toString(buffer); 491 return buffer.toString(); 492 } 493 494 495 496 /** 497 * Appends a string representation of this LDAP sync destination plugin to the 498 * provided buffer. 499 * 500 * @param buffer The buffer to which the string representation should be 501 * appended. 502 */ 503 public abstract void toString(final StringBuilder buffer); 504 505 506 507 /** 508 * {@inheritDoc} 509 */ 510 public Map<List<String>,String> getExamplesArgumentSets() 511 { 512 return Collections.emptyMap(); 513 } 514 }