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