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