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-2012 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    }