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; 034import java.util.concurrent.atomic.AtomicReference; 035 036import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 037import com.unboundid.directory.sdk.common.internal.Reconfigurable; 038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 039import com.unboundid.directory.sdk.sync.config.LDAPSyncSourcePluginConfig; 040import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 041import com.unboundid.directory.sdk.sync.types.PostStepResult; 042import com.unboundid.directory.sdk.sync.types.PreStepResult; 043import com.unboundid.directory.sdk.sync.types.SyncOperation; 044import com.unboundid.directory.sdk.sync.types.SyncServerContext; 045import com.unboundid.ldap.sdk.Entry; 046import com.unboundid.ldap.sdk.LDAPException; 047import com.unboundid.ldap.sdk.LDAPInterface; 048import com.unboundid.ldap.sdk.ResultCode; 049import com.unboundid.ldap.sdk.SearchRequest; 050import com.unboundid.ldap.sdk.SearchResultEntry; 051import com.unboundid.util.Extensible; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.args.ArgumentException; 055import com.unboundid.util.args.ArgumentParser; 056 057 058 059/** 060 * This class defines an API that must be implemented by extensions that 061 * perform processing on synchronization operations within an LDAP Sync 062 * Source. These extensions may be used to 063 * <ul> 064 * <li>Filter out certain changes from being synchronized.</li> 065 * <li>Change how an entry is fetched.</li> 066 * </ul> 067 * <BR> 068 * A note on exception handling: in general subclasses should not 069 * catch LDAPExceptions that are thrown when using the provided 070 * LDAPInterface unless there are specific exceptions that are 071 * expected. The Data Sync Server will handle 072 * LDAPExceptions in an appropriate way based on the specific 073 * cause of the exception. For example, some errors will result 074 * in the SyncOperation being retried, and others will trigger 075 * fail over to a different server. 076 * <BR> 077 * <H2>Configuring LDAP Sync Source Plugins</H2> 078 * In order to configure an LDAP sync source plugin created using this API, use 079 * a command like: 080 * <PRE> 081 * dsconfig create-sync-source-plugin \ 082 * --plugin-name "<I>{plugin-name}</I>" \ 083 * --type third-party-ldap \ 084 * --set "extension-class:<I>{class-name}</I>" \ 085 * --set "extension-argument:<I>{name=value}</I>" 086 * </PRE> 087 * where "<I>{plugin-name}</I>" is the name to use for the LDAP sync source 088 * plugin instance, "<I>{class-name}</I>" is the fully-qualified name of the 089 * Java class that extends 090 * {@code com.unboundid.directory.sdk.sync.api.LDAPSyncSourcePlugin}, and 091 * "<I>{name=value}</I>" represents name-value pairs for any arguments to 092 * provide to the LDAP sync source plugin. If multiple arguments should be 093 * provided to the LDAP sync source plugin, then the 094 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 095 * provided multiple times. 096 * 097 * @see com.unboundid.directory.sdk.sync.scripting.ScriptedLDAPSyncSourcePlugin 098 */ 099@Extensible() 100@SynchronizationServerExtension(appliesToLocalContent=false, 101 appliesToSynchronizedContent=true) 102@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE) 103public abstract class LDAPSyncSourcePlugin 104 implements UnboundIDExtension, 105 Reconfigurable<LDAPSyncSourcePluginConfig>, 106 ExampleUsageProvider 107{ 108 /** 109 * Creates a new instance of this LDAP sync source plugin. All sync 110 * source implementations must include a default constructor, but any 111 * initialization should generally be done in the 112 * {@code initializeLDAPSyncSourcePlugin} method. 113 */ 114 public LDAPSyncSourcePlugin() 115 { 116 // No implementation is required. 117 } 118 119 120 121 /** 122 * {@inheritDoc} 123 */ 124 public abstract String getExtensionName(); 125 126 127 128 /** 129 * {@inheritDoc} 130 */ 131 public abstract String[] getExtensionDescription(); 132 133 134 135 /** 136 * {@inheritDoc} 137 */ 138 public void defineConfigArguments(final ArgumentParser parser) 139 throws ArgumentException 140 { 141 // No arguments will be allowed by default. 142 } 143 144 145 146 /** 147 * Initializes this LDAP sync source plugin. This method will be called 148 * before any other methods in the class. 149 * 150 * @param serverContext A handle to the server context for the 151 * Data Sync Server in which this extension is 152 * running. Extensions should typically store this 153 * in a class member. 154 * @param config The general configuration for this proxy 155 * transformation. 156 * @param parser The argument parser which has been initialized from 157 * the configuration for this LDAP sync source 158 * plugin. 159 * 160 * @throws LDAPException If a problem occurs while initializing this ldap 161 * sync source plugin. 162 */ 163 public void initializeLDAPSyncSourcePlugin( 164 final SyncServerContext serverContext, 165 final LDAPSyncSourcePluginConfig config, 166 final ArgumentParser parser) 167 throws LDAPException 168 { 169 // No initialization will be performed by default. 170 } 171 172 173 174 /** 175 * {@inheritDoc} 176 */ 177 public boolean isConfigurationAcceptable( 178 final LDAPSyncSourcePluginConfig config, 179 final ArgumentParser parser, 180 final List<String> unacceptableReasons) 181 { 182 // No extended validation will be performed by default. 183 return true; 184 } 185 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 public ResultCode applyConfiguration(final LDAPSyncSourcePluginConfig config, 192 final ArgumentParser parser, 193 final List<String> adminActionsRequired, 194 final List<String> messages) 195 { 196 // By default, no configuration changes will be applied. 197 return ResultCode.SUCCESS; 198 } 199 200 201 202 /** 203 * Performs any cleanup which may be necessary when this LDAP sync source 204 * plugin is taken out of service. This can happen when it is deleted from 205 * the configuration and at server shutdown. 206 */ 207 public void finalizeLDAPSyncSourcePlugin() 208 { 209 // No implementation is required. 210 } 211 212 213 /** 214 * This method is called before each search for a source entry. If this method 215 * should only be run once per operation then an attachment can be added 216 * to the SyncOp for tracking A connection to the source server is provided. 217 * This method is overridden by plugins that need to manipulate an operation 218 * prior to source entry being fetched. 219 * 220 * @param sourceConnection A connection to the source server. 221 * @param searchRequest The search request that the LDAP Sync 222 * Source will use to fetch the entry. 223 * @param searchResults A list of entries that have been fetched. 224 * When the search criteria matches multiple 225 * entries, they should all be returned. A 226 * plugin that wishes to implement the fetch 227 * should put the fetched entries here and 228 * return 229 * @param operation The synchronization operation for this 230 * change. 231 * 232 * @return The result of the plugin processing. Be very careful when 233 * returning {@code PostStepResult#RETRY_OPERATION_UNLIMITED} as this 234 * can stall all in flight operations until this operation completes. 235 * This return value should only be used in situations where a 236 * remote service (e.g., the LDAP server) is unavailable. In this 237 * case, it's preferable to just throw the underlying LDAPException, 238 * which the Data Sync Server will handle correctly based on 239 * the type of the operation. 240 * 241 * @throws LDAPException In general subclasses should not catch 242 * LDAPExceptions that are thrown when 243 * using the LDAPInterface unless there 244 * are specific exceptions that are 245 * expected. The Data Sync Server 246 * will handle LDAPExceptions in an 247 * appropriate way based on the specific 248 * cause of the exception. For example, 249 * some errors will result in the 250 * SyncOperation being retried, and others 251 * will trigger fail over to a different 252 * server. Plugins should only throw 253 * LDAPException for errors related to 254 * communication with the LDAP server. 255 * Use the return code to indicate other 256 * types of errors, which might require 257 * retry. 258 */ 259 public PreStepResult preFetch(final LDAPInterface sourceConnection, 260 final SearchRequest searchRequest, 261 final List<SearchResultEntry> searchResults, 262 final SyncOperation operation) 263 throws LDAPException 264 { 265 return PreStepResult.CONTINUE; 266 } 267 268 269 /** 270 * This method is called after fetching a source entry. A 271 * connection to the source server is provided. This method is 272 * overridden by plugins that need to manipulate the search results that 273 * are returned to the Sync Pipe. This can include filtering out certain 274 * entries, remove information from the entries, or adding additional 275 * information, possibly by doing a followup LDAP search. 276 * 277 * @param sourceConnection A connection to the source server. 278 * @param fetchedEntryRef A reference to the entry that was fetched. 279 * This entry can be edited in place, or the 280 * reference can be changed to point to a 281 * different entry that the plugin constructs. 282 * The referenced entry might be {@code null} 283 * if no matched entry was found at the source. 284 * @param operation The synchronization operation for this 285 * change. 286 * 287 * @return The result of the plugin processing. Be very careful when 288 * returning {@code PostStepResult#RETRY_OPERATION_UNLIMITED} as this 289 * can stall all in flight operations until this operation completes. 290 * This return value should only be used in situations where a 291 * remote service (e.g., the LDAP server) is unavailable. In this 292 * case, it's preferable to just throw the underlying LDAPException, 293 * which the Data Sync Server will handle correctly based on 294 * the type of the operation. 295 * 296 * @throws LDAPException In general subclasses should not catch 297 * LDAPExceptions that are thrown when 298 * using the LDAPInterface unless there 299 * are specific exceptions that are 300 * expected. The Data Sync Server 301 * will handle LDAPExceptions in an 302 * appropriate way based on the specific 303 * cause of the exception. For example, 304 * some errors will result in the 305 * SyncOperation being retried, and others 306 * will trigger fail over to a different 307 * server. Plugins should only throw 308 * LDAPException for errors related to 309 * communication with the LDAP server. 310 * Use the return code to indicate other 311 * types of errors, which might require 312 * retry. 313 */ 314 public PostStepResult postFetch(final LDAPInterface sourceConnection, 315 final AtomicReference<Entry> fetchedEntryRef, 316 final SyncOperation operation) 317 throws LDAPException 318 { 319 return PostStepResult.CONTINUE; 320 } 321 322 323 324 /** 325 * Retrieves a string representation of this LDAP sync source plugin. 326 * 327 * @return A string representation of this LDAP sync source plugin. 328 */ 329 @Override() 330 public final String toString() 331 { 332 final StringBuilder buffer = new StringBuilder(); 333 toString(buffer); 334 return buffer.toString(); 335 } 336 337 338 339 /** 340 * Appends a string representation of this LDAP sync source plugin to the 341 * provided buffer. 342 * 343 * @param buffer The buffer to which the string representation should be 344 * appended. 345 */ 346 public abstract void toString(final StringBuilder buffer); 347 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 public Map<List<String>,String> getExamplesArgumentSets() 354 { 355 return Collections.emptyMap(); 356 } 357}