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.api;
028
029
030
031 import java.sql.SQLException;
032 import java.util.Collections;
033 import java.util.List;
034 import java.util.Map;
035
036 import com.unboundid.directory.sdk.common.internal.Configurable;
037 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
038 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039 import com.unboundid.directory.sdk.sync.config.JDBCSyncDestinationConfig;
040 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041 import com.unboundid.directory.sdk.sync.types.SyncOperation;
042 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
043 import com.unboundid.directory.sdk.sync.types.TransactionContext;
044 import com.unboundid.ldap.sdk.Entry;
045 import com.unboundid.ldap.sdk.Modification;
046 import com.unboundid.util.Extensible;
047 import com.unboundid.util.ThreadSafety;
048 import com.unboundid.util.ThreadSafetyLevel;
049 import com.unboundid.util.args.ArgumentException;
050 import com.unboundid.util.args.ArgumentParser;
051
052
053
054 /**
055 * This class defines an API that must be implemented by extensions
056 * in order to synchronize data into a relational database. Since the UnboundID
057 * Synchronization Server is LDAP-centric,
058 * this API allows you to take LDAP entries and split them out into
059 * database content and make the appropriate updates. The lifecycle of a sync
060 * operation is as follows:
061 * <ol>
062 * <li>Detect change at the synchronization source</li>
063 * <li>Fetch full source entry</li>
064 * <li>Perform any mappings and compute the equivalent destination entry</li>
065 * <li>Fetch full destination entry</li>
066 * <li>Diff the computed destination entry and actual destination entry</li>
067 * <li>Apply the minimal set of changes at the destination to bring it in sync
068 * </li>
069 * </ol>
070 * This implies that the {@link #fetchEntry(TransactionContext, Entry,
071 * SyncOperation)} method will
072 * always be called once prior to any of the other methods in this class.
073 * <p>
074 * In several places a {@link TransactionContext} is provided, which allows
075 * controlled access to the target database. By default, methods in this class
076 * are always provided with a fresh connection (i.e. a new transaction), and the
077 * Synchronization Server will always commit or rollback the transaction
078 * automatically, depending on whether the method returned normally or threw an
079 * exception. Implementers may optionally perform their own transaction
080 * management within these methods if necessary.
081 * <p>
082 * Several of these methods throw {@link SQLException}, which should be used in
083 * the case of any database access error. For other types of errors, runtime
084 * exceptions may be used (IllegalStateException, NullPointerException, etc.).
085 * The Synchronization Server will automatically retry operations that fail, up
086 * to a configurable amount of attempts. The exception to this rule is if a
087 * SQLException is thrown with a SQL state string beginning with "08"; this
088 * indicates a connection error, and in this case the operation is retried
089 * indefinitely.
090 * <BR>
091 * <H2>Configuring JDBC Sync Destinations</H2>
092 * In order to configure a JDBC sync destination based on this API and
093 * written in Java, use a command like:
094 * <PRE>
095 * dsconfig create-sync-destination \
096 * --destination-name "<I>{destination-name}</I>" \
097 * --type third-party-jdbc \
098 * --set "server:{server-name}" \
099 * --set "extension-class:<I>{class-name}</I>" \
100 * --set "extension-argument:<I>{name=value}</I>"
101 * </PRE>
102 * where "<I>{destination-name}</I>" is the name to use for the JDBC sync
103 * destination instance, "<I>{server-name}</I>" is the name of the JDBC external
104 * server that will be used as the sync destination, "<I>{class-name}</I>" is
105 * the fully-qualified name of the Java class written using this API, and
106 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
107 * provide to the JDBC sync destination. If multiple arguments should be
108 * provided to the JDBC sync destination, then the
109 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
110 * provided multiple times.
111 */
112 @Extensible()
113 @SynchronizationServerExtension(appliesToLocalContent=false,
114 appliesToSynchronizedContent=true)
115 public abstract class JDBCSyncDestination implements UnboundIDExtension,
116 Configurable,
117 ExampleUsageProvider
118 {
119 /**
120 * Creates a new instance of this JDBC Sync Destination. All sync
121 * destination implementations must include a default constructor, but any
122 * initialization should generally be done in the
123 * {@link #initializeJDBCSyncDestination} method.
124 */
125 public JDBCSyncDestination()
126 {
127 //no implementation is required
128 }
129
130
131
132 /**
133 * {@inheritDoc}
134 */
135 public abstract String getExtensionName();
136
137
138
139 /**
140 * {@inheritDoc}
141 */
142 public abstract String[] getExtensionDescription();
143
144
145
146 /**
147 * {@inheritDoc}
148 */
149 public Map<List<String>,String> getExamplesArgumentSets()
150 {
151 return Collections.emptyMap();
152 }
153
154
155
156 /**
157 * {@inheritDoc}
158 */
159 public void defineConfigArguments(final ArgumentParser parser)
160 throws ArgumentException
161 {
162 // No arguments will be allowed by default.
163 }
164
165
166
167 /**
168 * This hook is called when a Sync Pipe first starts up, or when the
169 * <i>resync</i> process first starts up. Any initialization of this sync
170 * destination should be performed here. This method should generally store
171 * the {@link SyncServerContext} in a class
172 * member so that it can be used elsewhere in the implementation.
173 * <p>
174 * A {@link TransactionContext} is provided, which allows
175 * controlled access to the target database. The context will contain a fresh
176 * fresh connection (i.e. a new transaction), and the Synchronization Server
177 * will always commit or rollback the transaction automatically, depending on
178 * whether this method returns normally or throws an exception. Implementers
179 * may optionally perform their own transaction management within this method
180 * if necessary.
181 * <p>
182 * The default implementation is empty.
183 *
184 * @param ctx
185 * a TransactionContext which provides a valid JDBC connection to the
186 * database.
187 * @param serverContext A handle to the server context for the server in
188 * which this extension is running.
189 * @param config The general configuration for this sync destination.
190 * @param parser The argument parser which has been initialized from
191 * the configuration for this JDBC sync destination.
192 */
193 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
194 public void initializeJDBCSyncDestination(
195 final TransactionContext ctx,
196 final SyncServerContext serverContext,
197 final JDBCSyncDestinationConfig config,
198 final ArgumentParser parser)
199 {
200 // No initialization will be performed by default.
201 }
202
203
204
205 /**
206 * This hook is called when a Sync Pipe shuts down, or when the <i>resync</i>
207 * process shuts down. Any clean-up of this sync destination should be
208 * performed here.
209 * <p>
210 * A {@link TransactionContext} is provided, which allows
211 * controlled access to the target database. The context will contain a fresh
212 * fresh connection (i.e. a new transaction), and the Synchronization Server
213 * will always commit or rollback the transaction automatically, depending on
214 * whether this method returns normally or throws an exception. Implementers
215 * may optionally perform their own transaction management within this method
216 * if necessary.
217 * <p>
218 * The default implementation is empty.
219 *
220 * @param ctx
221 * a TransactionContext which provides a valid JDBC connection to the
222 * database.
223 */
224 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
225 public void finalizeJDBCSyncDestination(
226 final TransactionContext ctx)
227 {
228 //No implementation required by default.
229 }
230
231
232
233 /**
234 * Return a full destination entry (in LDAP form) from the database,
235 * corresponding to the the source {@link Entry} that is passed in.
236 * This method should perform any queries necessary to gather the latest
237 * values for all the attributes to be synchronized and return them in an
238 * Entry.
239 * <p>
240 * Note that the if the source entry was renamed (see
241 * {@link SyncOperation#isModifyDN}), the
242 * <code>destEntryMappedFromSrc</code> will have the new DN; the old DN can
243 * be obtained by calling
244 * {@link SyncOperation#getDestinationEntryBeforeChange()} and getting the DN
245 * from there. This method should return the entry in its existing form
246 * (i.e. with the old DN, before it is changed).
247 * <p>
248 * A {@link TransactionContext} is provided, which allows
249 * controlled access to the target database. The context will contain a fresh
250 * fresh connection (i.e. a new transaction), and the Synchronization Server
251 * will always commit or rollback the transaction automatically, depending on
252 * whether this method returns normally or throws an exception. Implementers
253 * may optionally perform their own transaction management within this method
254 * if necessary.
255 * <p>
256 * This method <b>must be thread safe</b>, as it will be called repeatedly and
257 * concurrently by each of the Sync Pipe worker threads as they process
258 * entries.
259 *
260 * @param ctx
261 * a TransactionContext which provides a valid JDBC connection to the
262 * database.
263 * @param destEntryMappedFromSrc
264 * the LDAP entry which corresponds to the database "entry" to fetch
265 * @param operation
266 * the sync operation for this change
267 * @return a full LDAP Entry, or null if no such entry exists.
268 * @throws SQLException
269 * if there is an error fetching the entry
270 */
271 @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
272 public abstract Entry fetchEntry(final TransactionContext ctx,
273 final Entry destEntryMappedFromSrc,
274 final SyncOperation operation)
275 throws SQLException;
276
277
278
279 /**
280 * Creates a full database "entry", corresponding to the the LDAP
281 * {@link Entry} that is passed in. This method should perform any inserts and
282 * updates necessary to make sure the entry is fully created on the database.
283 * <p>
284 * A {@link TransactionContext} is provided, which allows
285 * controlled access to the target database. The context will contain a fresh
286 * fresh connection (i.e. a new transaction), and the Synchronization Server
287 * will always commit or rollback the transaction automatically, depending on
288 * whether this method returns normally or throws an exception. Implementers
289 * may optionally perform their own transaction management within this method
290 * if necessary.
291 * <p>
292 * This method <b>must be thread safe</b>, as it will be called repeatedly and
293 * concurrently by the Sync Pipe worker threads as they process CREATE
294 * operations.
295 *
296 * @param ctx
297 * a TransactionContext which provides a valid JDBC connection to the
298 * database.
299 * @param entryToCreate
300 * the LDAP entry which corresponds to the database "entry" to create
301 * @param operation
302 * the sync operation for this change
303 * @throws SQLException
304 * if there is an error creating the entry
305 */
306 @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
307 public abstract void createEntry(final TransactionContext ctx,
308 final Entry entryToCreate,
309 final SyncOperation operation)
310 throws SQLException;
311
312
313
314 /**
315 * Modify an "entry" in the database, corresponding to the the LDAP
316 * {@link Entry} that is passed in. This method may perform multiple updates
317 * (including inserting or deleting rows) in order to fully synchronize the
318 * entire entry on the database.
319 * <p>
320 * Note that the if the source entry was renamed (see
321 * {@link SyncOperation#isModifyDN}), the <code>fetchedDestEntry</code> will
322 * have the old DN; the new DN can be obtained by calling
323 * {@link SyncOperation#getDestinationEntryAfterChange()} and getting the DN
324 * from there.
325 * <p>
326 * A {@link TransactionContext} is provided, which allows
327 * controlled access to the target database. The context will contain a fresh
328 * fresh connection (i.e. a new transaction), and the Synchronization Server
329 * will always commit or rollback the transaction automatically, depending on
330 * whether this method returns normally or throws an exception. Implementers
331 * may optionally perform their own transaction management within this method
332 * if necessary.
333 * <p>
334 * This method <b>must be thread safe</b>, as it will be called repeatedly and
335 * concurrently by the Sync Pipe worker threads as they process MODIFY
336 * operations.
337 * @param ctx
338 * a TransactionContext which provides a valid JDBC connection to the
339 * database.
340 * @param fetchedDestEntry
341 * the LDAP entry which corresponds to the database "entry" to modify
342 * @param modsToApply
343 * a list of Modification objects which should be applied
344 * @param operation
345 * the sync operation for this change
346 * @throws SQLException
347 * if there is an error modifying the entry
348 */
349 @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
350 public abstract void modifyEntry(final TransactionContext ctx,
351 final Entry fetchedDestEntry,
352 final List<Modification> modsToApply,
353 final SyncOperation operation)
354 throws SQLException;
355
356
357
358 /**
359 * Delete a full "entry" from the database, corresponding to the the LDAP
360 * {@link Entry} that is passed in. This method may perform multiple deletes
361 * or updates if necessary to fully delete the entry from the database.
362 * <p>
363 * A {@link TransactionContext} is provided, which allows
364 * controlled access to the target database. The context will contain a fresh
365 * fresh connection (i.e. a new transaction), and the Synchronization Server
366 * will always commit or rollback the transaction automatically, depending on
367 * whether this method returns normally or throws an exception. Implementers
368 * may optionally perform their own transaction management within this method
369 * if necessary.
370 * <p>
371 * This method <b>must be thread safe</b>, as it will be called repeatedly and
372 * concurrently by the Sync Pipe worker threads as they process DELETE
373 * operations.
374 *
375 * @param ctx
376 * a TransactionContext which provides a valid JDBC connection to the
377 * database.
378 * @param fetchedDestEntry
379 * the LDAP entry which corresponds to the database "entry" to delete
380 * @param operation
381 * the sync operation for this change
382 * @throws SQLException
383 * if there is an error deleting the entry
384 */
385 @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
386 public abstract void deleteEntry(final TransactionContext ctx,
387 final Entry fetchedDestEntry,
388 final SyncOperation operation)
389 throws SQLException;
390 }