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.scripting;
028
029
030
031 import java.util.List;
032
033 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034 import com.unboundid.directory.sdk.sync.config.SyncPipePluginConfig;
035 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
036 import com.unboundid.directory.sdk.sync.types.PostStepResult;
037 import com.unboundid.directory.sdk.sync.types.PreStepResult;
038 import com.unboundid.directory.sdk.sync.types.SyncOperation;
039 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
040 import com.unboundid.ldap.sdk.Entry;
041 import com.unboundid.ldap.sdk.LDAPException;
042 import com.unboundid.ldap.sdk.ResultCode;
043 import com.unboundid.util.Extensible;
044 import com.unboundid.util.ThreadSafety;
045 import com.unboundid.util.ThreadSafetyLevel;
046 import com.unboundid.util.args.ArgumentException;
047 import 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)
094 public 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 }