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