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.Configurable;
036    import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037    import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038    import com.unboundid.directory.sdk.sync.config.SyncDestinationConfig;
039    import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
040    import com.unboundid.directory.sdk.sync.types.EndpointException;
041    import com.unboundid.directory.sdk.sync.types.SyncOperation;
042    import com.unboundid.directory.sdk.sync.types.SyncServerContext;
043    import com.unboundid.ldap.sdk.Entry;
044    import com.unboundid.ldap.sdk.Modification;
045    import com.unboundid.util.Extensible;
046    import com.unboundid.util.ThreadSafety;
047    import com.unboundid.util.ThreadSafetyLevel;
048    import com.unboundid.util.args.ArgumentException;
049    import com.unboundid.util.args.ArgumentParser;
050    
051    
052    
053    /**
054     * This class defines an API that must be implemented by extensions that
055     * wish to push changes processed by the Synchronization Server to an arbitrary
056     * destination. This type of sync destination is generic and can support a wide
057     * range of endpoints. In addition, this type of sync destination supports
058     * one-way notifications, where the source and destination entries are never
059     * compared but instead changes are pushed straight through.
060     *
061     * <H2>Configuring Sync Destinations</H2>
062     * In order to configure a sync destination created using this API, use
063     * a command like:
064     * <PRE>
065     *      dsconfig create-sync-destination \
066     *           --sync-destination-name "<I>{name}</I>" \
067     *           --type third-party \
068     *           --set "extension-class:<I>{class-name}</I>" \
069     *           --set "extension-argument:<I>{name=value}</I>"
070     * </PRE>
071     * where "<I>{name}</I>" is the name to use for the sync destination
072     * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
073     * that extends
074     * {@code com.unboundid.directory.sdk.sync.api.SyncDestination},
075     * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
076     * provide to the sync destination. If multiple arguments should be
077     * provided to extension, then the
078     * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
079     * provided multiple times.
080     *
081     * @see
082     *    com.unboundid.directory.sdk.sync.scripting.ScriptedSyncDestination
083     */
084    @Extensible()
085    @SynchronizationServerExtension(appliesToLocalContent=false,
086                                    appliesToSynchronizedContent=true)
087    @ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
088    public abstract class SyncDestination
089           implements UnboundIDExtension,
090                      Configurable,
091                      ExampleUsageProvider
092    {
093      /**
094       * Creates a new instance of this notification destination.  All
095       * implementations must include a default constructor, but any
096       * initialization should generally be done in the
097       * {@link #initializeSyncDestination} method.
098       */
099      public SyncDestination()
100      {
101        // No implementation is required.
102      }
103    
104    
105    
106      /**
107       * {@inheritDoc}
108       */
109      public abstract String getExtensionName();
110    
111    
112    
113      /**
114       * {@inheritDoc}
115       */
116      public abstract String[] getExtensionDescription();
117    
118    
119    
120      /**
121       * {@inheritDoc}
122       */
123      public Map<List<String>,String> getExamplesArgumentSets()
124      {
125        return Collections.emptyMap();
126      }
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 destination. This hook is called when a Sync Pipe
143       * first starts up, or when the <i>resync</i> process first starts up. Any
144       * initialization should be performed here. This method should generally store
145       * the {@link SyncServerContext} in a class
146       * member so that it can be used elsewhere in the implementation.
147       * <p>
148       * The default implementation is empty.
149       *
150       * @param  serverContext  A handle to the server context for the server in
151       *                        which this extension is running. Extensions should
152       *                        typically store this in a class member.
153       * @param  config         The general configuration for this object.
154       * @param  parser         The argument parser which has been initialized from
155       *                        the configuration for this sync destination.
156       * @throws  EndpointException
157       *                        if a problem occurs while initializing this
158       *                        sync destination.
159       */
160      public void initializeSyncDestination(
161                                         final SyncServerContext serverContext,
162                                         final SyncDestinationConfig config,
163                                         final ArgumentParser parser)
164                                           throws EndpointException
165      {
166        // No initialization will be performed by default.
167      }
168    
169    
170    
171      /**
172       * This hook is called when a Sync Pipe shuts down, or when the <i>resync</i>
173       * process shuts down. Any clean-up of this sync destination should be
174       * performed here.
175       * <p>
176       * The default implementation is empty.
177       */
178      public void finalizeSyncDestination()
179      {
180        // No implementation is performed by default.
181      }
182    
183    
184    
185      /**
186       * Return the URL or path identifying the destination endpoint
187       * to which this extension is transmitting data. This is used for logging
188       * purposes only, so it could just be a server name or hostname and port, etc.
189       *
190       * @return the path to the destination endpoint
191       */
192      public abstract String getCurrentEndpointURL();
193    
194    
195    
196      /**
197       * Return a full destination entry (in LDAP form) from the destination
198       * endpoint, corresponding to the the source {@link Entry} that is passed in.
199       * This method should perform any queries necessary to gather the latest
200       * values for all the attributes to be synchronized and return them in an
201       * Entry.
202       * <p>
203       * This method only needs to be implemented if the 'synchronization-mode' on
204       * the Sync Pipe is set to 'standard'. If it is set to 'notification', this
205       * method will never be called, and the pipe will pass changes straight
206       * through to one of {@link #createEntry}, {@link #modifyEntry}, or
207       * {@link #deleteEntry}.
208       * <p>
209       * Note that the if the source entry was renamed (see
210       * {@link SyncOperation#isModifyDN}), the
211       * <code>destEntryMappedFromSrc</code> will have the new DN; the old DN can
212       * be obtained by calling
213       * {@link SyncOperation#getDestinationEntryBeforeChange()} and getting the DN
214       * from there. This method should return the entry in its existing form
215       * (i.e. with the old DN, before it is changed).
216       * <p>
217       * This method <b>must be thread safe</b>, as it will be called repeatedly and
218       * concurrently by each of the Sync Pipe worker threads as they process
219       * entries.
220       * @param destEntryMappedFromSrc
221       *          the LDAP entry which corresponds to the destination "entry" to
222       *          fetch
223       * @param  operation
224       *          the sync operation for this change
225       * @return a list containing the full LDAP entries that matched this search
226       *          (there may be more than one), or an empty list if no such entry
227       *          exists
228       * @throws EndpointException
229       *           if there is an error fetching the entry
230       */
231      public List<Entry> fetchEntry(final Entry destEntryMappedFromSrc,
232                                    final SyncOperation operation)
233                                      throws EndpointException
234      {
235        throw new UnsupportedOperationException(
236                "The fetchEntry() method must be implemented in the '" +
237                getExtensionName() + "' extension if the Sync Pipe is " +
238                "running in standard mode (see 'synchronization-mode' " +
239                "in the Sync Pipe configuration).");
240      }
241    
242    
243    
244      /**
245       * Creates a full destination "entry", corresponding to the the LDAP
246       * {@link Entry} that is passed in. This method is responsible for
247       * transforming the contents of the entry into the desired format and
248       * transmitting it to the target destination. It should perform any inserts or
249       * updates necessary to make sure the entry is fully created on the
250       * destination endpoint.
251       * <p>
252       * This method <b>must be thread safe</b>, as it will be called repeatedly and
253       * concurrently by the Sync Pipe worker threads as they process CREATE
254       * operations.
255       * @param entryToCreate
256       *          the LDAP entry which corresponds to the destination
257       *          "entry" to create
258       * @param  operation
259       *          the sync operation for this change
260       * @throws EndpointException
261       *           if there is an error creating the entry
262       */
263      public abstract void createEntry(final Entry entryToCreate,
264                                       final SyncOperation operation)
265                                          throws EndpointException;
266    
267    
268    
269      /**
270       * Modify an "entry" on the destination, corresponding to the the LDAP
271       * {@link Entry} that is passed in. This method is responsible for
272       * transforming the contents of the entry into the desired format and
273       * transmitting it to the target destination. It may perform multiple updates
274       * (including inserting or deleting other attributes) in order to fully
275       * synchronize the entire entry on the destination endpoint.
276       * <p>
277       * Note that the if the source entry was renamed (see
278       * {@link SyncOperation#isModifyDN}), the
279       * <code>fetchedDestEntry</code> will have the old DN; the new DN can
280       * be obtained by calling
281       * {@link SyncOperation#getDestinationEntryAfterChange()} and getting the DN
282       * from there.
283       * <p>
284       * This method <b>must be thread safe</b>, as it will be called repeatedly and
285       * concurrently by the Sync Pipe worker threads as they process MODIFY
286       * operations.
287       * @param entryToModify
288       *          the LDAP entry which corresponds to the destination
289       *          "entry" to modify. If the synchronization mode is 'standard',
290       *          this will be the entry that was returned by {@link #fetchEntry};
291       *          otherwise if the synchronization mode is 'notification', this
292       *          will be the destination entry mapped from the source entry, before
293       *          changes are applied.
294       * @param modsToApply
295       *          a list of Modification objects which should be applied; these will
296       *          have any configured attribute mappings already applied
297       * @param  operation
298       *          the sync operation for this change
299       * @throws EndpointException
300       *           if there is an error modifying the entry
301       */
302      public abstract void modifyEntry(final Entry entryToModify,
303                                       final List<Modification> modsToApply,
304                                       final SyncOperation operation)
305                                              throws EndpointException;
306    
307    
308    
309      /**
310       * Delete a full "entry" from the destination, corresponding to the the LDAP
311       * {@link Entry} that is passed in. This method may perform multiple deletes
312       * or updates if necessary to fully delete the entry from the destination
313       * endpoint.
314       * <p>
315       * This method <b>must be thread safe</b>, as it will be called repeatedly and
316       * concurrently by the Sync Pipe worker threads as they process DELETE
317       * operations.
318       * @param entryToDelete
319       *          the LDAP entry which corresponds to the destination
320       *          "entry" to delete. If the synchronization mode is 'standard',
321       *          this will be the entry that was returned by {@link #fetchEntry};
322       *          otherwise if the synchronization mode is 'notification', this
323       *          will be the mapped destination entry.
324       * @param  operation
325       *          the sync operation for this change
326       * @throws EndpointException
327       *           if there is an error deleting the entry
328       */
329      public abstract void deleteEntry(final Entry entryToDelete,
330                                       final SyncOperation operation)
331                                            throws EndpointException;
332    }