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