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-2013 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.SyncPipePluginConfig;
039 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
040 import com.unboundid.directory.sdk.sync.types.PostStepResult;
041 import com.unboundid.directory.sdk.sync.types.PreStepResult;
042 import com.unboundid.directory.sdk.sync.types.SyncOperation;
043 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
044 import com.unboundid.ldap.sdk.Entry;
045 import com.unboundid.ldap.sdk.LDAPException;
046 import com.unboundid.ldap.sdk.ResultCode;
047 import com.unboundid.util.Extensible;
048 import com.unboundid.util.ThreadSafety;
049 import com.unboundid.util.ThreadSafetyLevel;
050 import com.unboundid.util.args.ArgumentException;
051 import 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)
099 public 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 * Synchronization 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 }