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.SyncPipePluginConfig; 039import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 040import com.unboundid.directory.sdk.sync.types.PostStepResult; 041import com.unboundid.directory.sdk.sync.types.PreStepResult; 042import com.unboundid.directory.sdk.sync.types.SyncOperation; 043import com.unboundid.directory.sdk.sync.types.SyncServerContext; 044import com.unboundid.ldap.sdk.Entry; 045import com.unboundid.ldap.sdk.LDAPException; 046import com.unboundid.ldap.sdk.ResultCode; 047import com.unboundid.util.Extensible; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050import com.unboundid.util.args.ArgumentException; 051import com.unboundid.util.args.ArgumentParser; 052 053 054 055/** 056 * This class defines an API that must be implemented by extensions that 057 * perform processing on synchronization operations within the Sync Pipe. These 058 * extensions may be used to 059 * <ul> 060 * <li>Filter out certain changes from being synchronized.</li> 061 * <li>Add and remove attributes that should be synchronized with the 062 * destination independent of whether they changed at the source or 063 * not.</li> 064 * <li>Manipulate the changes that are synchronized to ignore certain 065 * modified attributes or change the representation of modified 066 * attributes.</li> 067 * <li>Skip certain steps in Sync Pipe processing, e.g. attribute 068 * and DN mapping.</li> 069 * </ul> 070 * Most plugins will need to override the {@code postMapping} method but not 071 * the {@code preMapping} method. These extensions do not have access to the 072 * Sync Source or Sync Destination. 073 * <BR> 074 * <H2>Configuring Sync Pipe Plugins</H2> 075 * In order to configure a sync pipe plugin created using this API, use a 076 * command like: 077 * <PRE> 078 * dsconfig create-sync-pipe-plugin \ 079 * --plugin-name "<I>{plugin-name}</I>" \ 080 * --type third-party \ 081 * --set "extension-class:<I>{class-name}</I>" \ 082 * --set "extension-argument:<I>{name=value}</I>" 083 * </PRE> 084 * where "<I>{plugin-name}</I>" is the name to use for the sync pipe plugin 085 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class 086 * that extends {@code com.unboundid.directory.sdk.sync.api.SyncPipePlugin}, 087 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to 088 * provide to the sync pipe plugin. If multiple arguments should be provided to 089 * the sync pipe plugin, then the 090 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 091 * provided multiple times. 092 * 093 * @see com.unboundid.directory.sdk.sync.scripting.ScriptedSyncPipePlugin 094 */ 095@Extensible() 096@SynchronizationServerExtension(appliesToLocalContent=false, 097 appliesToSynchronizedContent=true) 098@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE) 099public abstract class SyncPipePlugin 100 implements UnboundIDExtension, Reconfigurable<SyncPipePluginConfig>, 101 ExampleUsageProvider 102{ 103 /** 104 * Creates a new instance of this sync pipe plugin. All sync pipe plugin 105 * implementations must include a default constructor, but any 106 * initialization should generally be done in the 107 * {@code initializeSyncPipePlugin} method. 108 */ 109 public SyncPipePlugin() 110 { 111 // No implementation is required. 112 } 113 114 115 116 /** 117 * {@inheritDoc} 118 */ 119 public abstract String getExtensionName(); 120 121 122 123 /** 124 * {@inheritDoc} 125 */ 126 public abstract String[] getExtensionDescription(); 127 128 129 130 /** 131 * {@inheritDoc} 132 */ 133 public void defineConfigArguments(final ArgumentParser parser) 134 throws ArgumentException 135 { 136 // No arguments will be allowed by default. 137 } 138 139 140 141 /** 142 * Initializes this sync pipe plugin. This method will be called before 143 * any other methods in the class. 144 * 145 * @param serverContext A handle to the server context for the 146 * Data Sync Server in which this extension is 147 * running. Extensions should typically store this 148 * in a class member. 149 * @param config The general configuration for this proxy 150 * transformation. 151 * @param parser The argument parser which has been initialized from 152 * the configuration for this sync pipe plugin. 153 * 154 * @throws LDAPException If a problem occurs while initializing this sync 155 * pipe plugin. 156 */ 157 public void initializeSyncPipePlugin(final SyncServerContext serverContext, 158 final SyncPipePluginConfig config, 159 final ArgumentParser parser) 160 throws LDAPException 161 { 162 // No initialization will be performed by default. 163 } 164 165 166 167 /** 168 * {@inheritDoc} 169 */ 170 public boolean isConfigurationAcceptable( 171 final SyncPipePluginConfig config, 172 final ArgumentParser parser, 173 final List<String> unacceptableReasons) 174 { 175 // No extended validation will be performed by default. 176 return true; 177 } 178 179 180 181 /** 182 * {@inheritDoc} 183 */ 184 public ResultCode applyConfiguration(final SyncPipePluginConfig config, 185 final ArgumentParser parser, 186 final List<String> adminActionsRequired, 187 final List<String> messages) 188 { 189 // By default, no configuration changes will be applied. 190 return ResultCode.SUCCESS; 191 } 192 193 194 195 /** 196 * Performs any cleanup which may be necessary when this sync pipe plugin 197 * is taken out of service. This can happen when it is deleted from the 198 * configuration and at server shutdown. 199 */ 200 public void finalizeSyncPipePlugin() 201 { 202 // No implementation is required. 203 } 204 205 206 207 /** 208 * This method is called immediately before the attributes and DN in 209 * the source entry are mapped into the equivalent destination entry. 210 * This equivalent destination entry is then compared to the actual 211 * destination entry to determine which of the modified attributes need 212 * to be updated. 213 * <p> 214 * This method is typically used to either filter out certain changes 215 * (by returning {@link PreStepResult#ABORT_OPERATION}) or to manipulate the 216 * source entry before it is converted into an equivalent destination entry. 217 * Attributes that will not otherwise be affected by attribute mapping 218 * can be set in {@code equivalentDestinationEntry}. Although, due to the 219 * possibility of being overwritten in the mapping phase, manipulation of 220 * {@code equivalentDestinationEntry} is typically reserved for the 221 * {@link #postMapping} method. 222 * <p> 223 * The set of source attributes that should be synchronized at the destination 224 * can be manipulated by calling 225 * {@link SyncOperation#addModifiedSourceAttribute} and 226 * {@link SyncOperation#removeModifiedSourceAttribute} on 227 * {@code operation}. 228 * <p> 229 * Additional steps must be taken if this plugin adds destination attributes 230 * in {@code equivalentDestinationEntry} that need to be modified at the 231 * destination. For operations with an operation type of 232 * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#CREATE}, 233 * any updates made to 234 * {@code equivalentDestinationEntry} will be included in the 235 * entry created at the destination. However, for operations with an 236 * operation type of 237 * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#MODIFY}, 238 * destination attributes 239 * added by this plugin that need to be modified must be updated 240 * explicitly by calling 241 * {@link SyncOperation#addModifiedDestinationAttribute}. 242 * <p> 243 * With the exception of aborting changes or skipping the mapping step 244 * completely, most plugins will not need to override this method since 245 * the {@link #postMapping} method has access to the fully mapped destination 246 * entry. 247 * 248 * @param sourceEntry The entry that was fetched from the 249 * source. 250 * @param equivalentDestinationEntry The destination entry that is 251 * equivalent to the source. This entry 252 * will be empty except for any 253 * modifications that have been performed 254 * by other sync pipe plugins. 255 * @param operation The operation that is being 256 * synchronized. 257 * 258 * @return The result of the plugin processing. Note: 259 * {@code PreStepResult#3SKIP_CURRENT_STEP} should only be returned 260 * if this plugin takes responsibility for fully constructing the 261 * equivalent destination entry. 262 */ 263 public PreStepResult preMapping(final Entry sourceEntry, 264 final Entry equivalentDestinationEntry, 265 final SyncOperation operation) 266 { 267 return PreStepResult.CONTINUE; 268 } 269 270 271 272 /** 273 * This method is called immediately after the attributes and DN in 274 * the source entry are mapped into the equivalent destination entry. 275 * Once this mapping is complete, this equivalent destination entry is then 276 * compared to the actual destination entry to determine which of the modified 277 * attributes need to be updated. 278 * <p> 279 * This method is typically used to manipulate the equivalent destination 280 * entry before these necessary changes are calculated. It can also be used 281 * to filter out certain changes (by returning 282 * {@link PostStepResult#ABORT_OPERATION}), but this is typically done in 283 * the {@link #preMapping} method. 284 * <p> 285 * The set of source attributes that should be synchronized at the destination 286 * can be manipulated by calling 287 * {@link SyncOperation#addModifiedSourceAttribute} and 288 * {@link SyncOperation#removeModifiedSourceAttribute} on 289 * {@code operation}. 290 * <p> 291 * Additional steps must be taken if this plugin adds destination attributes 292 * in {@code equivalentDestinationEntry} that need to be modified at the 293 * destination. For operations with an operation type of 294 * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#CREATE}, 295 * any updates made to 296 * {@code equivalentDestinationEntry} will be included in the 297 * entry created at the destination. However, for operations with an 298 * operation type of 299 * {@link com.unboundid.directory.sdk.sync.types.SyncOperationType#MODIFY}, 300 * destination attributes 301 * added by this plugin that need to be modified must be updated 302 * explicitly by calling 303 * {@link SyncOperation#addModifiedDestinationAttribute}. 304 * <p> 305 * With the exception of aborting changes or skipping the mapping step 306 * completely, most plugins will override this method instead of 307 * {@link #preMapping} because this method has access to the fully mapped 308 * destination entry. 309 * 310 * @param sourceEntry The entry that was fetched from the 311 * source. 312 * @param equivalentDestinationEntry The destination entry that is 313 * equivalent to the source. This entry 314 * will include all attributes mapped 315 * from the source entry. 316 * @param operation The operation that is being 317 * synchronized. 318 * 319 * @return The result of the plugin processing. 320 */ 321 public PostStepResult postMapping(final Entry sourceEntry, 322 final Entry equivalentDestinationEntry, 323 final SyncOperation operation) 324 { 325 return PostStepResult.CONTINUE; 326 } 327 328 329 330 /** 331 * Retrieves a string representation of this sync pipe plugin. 332 * 333 * @return A string representation of this sync pipe plugin. 334 */ 335 @Override() 336 public final String toString() 337 { 338 final StringBuilder buffer = new StringBuilder(); 339 toString(buffer); 340 return buffer.toString(); 341 } 342 343 344 345 /** 346 * Appends a string representation of this sync pipe plugin to the provided 347 * buffer. 348 * 349 * @param buffer The buffer to which the string representation should be 350 * appended. 351 */ 352 public abstract void toString(final StringBuilder buffer); 353 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 public Map<List<String>,String> getExamplesArgumentSets() 360 { 361 return Collections.emptyMap(); 362 } 363}