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.io.Serializable;
032 import java.sql.SQLException;
033 import java.util.Collections;
034 import java.util.Iterator;
035 import java.util.LinkedList;
036 import java.util.List;
037 import java.util.Map;
038 import java.util.concurrent.BlockingQueue;
039 import java.util.concurrent.atomic.AtomicLong;
040
041 import com.unboundid.directory.sdk.common.internal.Configurable;
042 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
043 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
044 import com.unboundid.directory.sdk.sync.config.JDBCSyncSourceConfig;
045 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
046 import com.unboundid.directory.sdk.sync.types.DatabaseChangeRecord;
047 import com.unboundid.directory.sdk.sync.types.SetStartpointOptions;
048 import com.unboundid.directory.sdk.sync.types.SyncOperation;
049 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
050 import com.unboundid.directory.sdk.sync.types.TransactionContext;
051 import com.unboundid.ldap.sdk.Entry;
052 import com.unboundid.util.Extensible;
053 import com.unboundid.util.ThreadSafety;
054 import com.unboundid.util.ThreadSafetyLevel;
055 import com.unboundid.util.args.ArgumentException;
056 import com.unboundid.util.args.ArgumentParser;
057
058
059
060 /**
061 * This class defines an API that must be implemented by extensions
062 * in order to synchronize data out of a relational database. Since the
063 * UnboundID Synchronization Server is LDAP-centric,
064 * this API allows you to take database content and convert it into LDAP
065 * entries which can then be processed by the Synchronization Server. The
066 * lifecycle of
067 * a sync operation is as follows:
068 * <ol>
069 * <li>Detect change at the synchronization source</li>
070 * <li>Fetch full source entry</li>
071 * <li>Perform any mappings and compute the equivalent destination entry</li>
072 * <li>Fetch full destination entry</li>
073 * <li>Diff the computed destination entry and actual (fetched) destination
074 * entry</li>
075 * <li>Apply the minimal set of changes at the destination to bring it in sync
076 * </li>
077 * </ol>
078 * This implies that the
079 * {@link #fetchEntry(TransactionContext, SyncOperation)} method will be
080 * called once for every change that is returned by
081 * {@link #getNextBatchOfChanges(TransactionContext, int, AtomicLong)}.
082 * <p>
083 * In several places a {@link TransactionContext} is provided, which allows
084 * controlled access to the target database. By default, methods in this class
085 * are always provided with a fresh connection (i.e. a new transaction), and the
086 * Synchronization Server will always commit or rollback the transaction
087 * automatically, depending on whether the method returned normally or threw an
088 * exception. Implementers may optionally perform their own transaction
089 * management within these methods if necessary.
090 * <p>
091 * Several of these methods throw {@link SQLException}, which should be used in
092 * the case of any database access error. For other types of errors, runtime
093 * exceptions may be used (IllegalStateException, NullPointerException, etc.).
094 * The Synchronization Server will automatically retry operations that fail, up
095 * to a configurable amount of attempts. The exception to this rule is if a
096 * SQLException is thrown with a SQL state string beginning with "08"; this
097 * indicates a connection error, and in this case the operation is retried
098 * indefinitely.
099 * <BR>
100 * <H2>Configuring JDBC Sync Sources</H2>
101 * In order to configure a JDBC sync source based on this API and
102 * written in Java, use a command like:
103 * <PRE>
104 * dsconfig create-sync-source \
105 * --source-name "<I>{source-name}</I>" \
106 * --type third-party-jdbc \
107 * --set "server:{server-name}" \
108 * --set "extension-class:<I>{class-name}</I>" \
109 * --set "extension-argument:<I>{name=value}</I>"
110 * </PRE>
111 * where "<I>{source-name}</I>" is the name to use for the JDBC sync source
112 * instance, "<I>{server-name}</I>" is the name of the JDBC external server that
113 * will be used as the sync source, "<I>{class-name}</I>" is the fully-qualified
114 * name of the Java class written using this API, and "<I>{name=value}</I>"
115 * represents name-value pairs for any arguments to provide to the JDBC sync
116 * source. If multiple arguments should be provided to the JDBC sync source,
117 * then the "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option
118 * should be provided multiple times.
119 */
120 @Extensible()
121 @SynchronizationServerExtension(appliesToLocalContent=false,
122 appliesToSynchronizedContent=true)
123 public abstract class JDBCSyncSource
124 implements UnboundIDExtension,
125 Configurable,
126 ExampleUsageProvider
127 {
128 /**
129 * Creates a new instance of this JDBC Sync Source. All sync
130 * source implementations must include a default constructor, but any
131 * initialization should generally be done in the
132 * {@link #initializeJDBCSyncSource} method.
133 */
134 public JDBCSyncSource()
135 {
136 // No implementation is required.
137 }
138
139
140
141 /**
142 * {@inheritDoc}
143 */
144 public abstract String getExtensionName();
145
146
147
148 /**
149 * {@inheritDoc}
150 */
151 public abstract String[] getExtensionDescription();
152
153
154
155 /**
156 * {@inheritDoc}
157 */
158 public Map<List<String>,String> getExamplesArgumentSets()
159 {
160 return Collections.emptyMap();
161 }
162
163
164
165 /**
166 * {@inheritDoc}
167 */
168 public void defineConfigArguments(final ArgumentParser parser)
169 throws ArgumentException
170 {
171 // No arguments will be allowed by default.
172 }
173
174
175
176 /**
177 * This hook is called when a Sync Pipe first starts up, when the
178 * <i>resync</i> process first starts up, or when the set-startpoint
179 * subcommand is called from the <i>realtime-sync</i> command line tool.
180 * Any initialization of this sync source should be performed here. This
181 * method should generally store the {@link SyncServerContext} in a class
182 * member so that it can be used elsewhere in the implementation.
183 * <p>
184 * A {@link TransactionContext} is provided, which allows
185 * controlled access to the target database. The context will contain a fresh
186 * fresh connection (i.e. a new transaction), and the Synchronization Server
187 * will always commit or rollback the transaction automatically, depending on
188 * whether this method returns normally or throws an exception. Implementers
189 * may optionally perform their own transaction management within this method
190 * if necessary.
191 * <p>
192 * The default implementation is empty.
193 *
194 * @param ctx
195 * a TransactionContext which provides a valid JDBC connection to the
196 * database.
197 * @param serverContext A handle to the server context for the server in
198 * which this extension is running.
199 * @param config The general configuration for this sync source.
200 * @param parser The argument parser which has been initialized from
201 * the configuration for this JDBC sync source.
202 */
203 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
204 public void initializeJDBCSyncSource(final TransactionContext ctx,
205 final SyncServerContext serverContext,
206 final JDBCSyncSourceConfig config,
207 final ArgumentParser parser)
208 {
209 // No initialization will be performed by default.
210 }
211
212
213
214 /**
215 * This hook is called when a Sync Pipe shuts down, when the <i>resync</i>
216 * process shuts down, or when the set-startpoint subcommand (from the
217 * <i>realtime-sync</i> command line tool) is finished. Any clean up of this
218 * sync source should be performed here.
219 * <p>
220 * A {@link TransactionContext} is provided, which allows
221 * controlled access to the target database. The context will contain a fresh
222 * fresh connection (i.e. a new transaction), and the Synchronization Server
223 * will always commit or rollback the transaction automatically, depending on
224 * whether this method returns normally or throws an exception. Implementers
225 * may optionally perform their own transaction management within this method
226 * if necessary.
227 * <p>
228 * The default implementation is empty.
229 *
230 * @param ctx
231 * a TransactionContext which provides a valid JDBC connection to the
232 * database.
233 */
234 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
235 public void finalizeJDBCSyncSource(final TransactionContext ctx)
236 {
237 //No implementation required by default.
238 }
239
240
241
242 /**
243 * This method should effectively set the starting point for synchronization
244 * to the place specified by the <code>options</code> parameter. This should
245 * cause all changes previous to the specified start point to be disregarded
246 * and only changes after that point to be returned by
247 * {@link #getNextBatchOfChanges(TransactionContext, int, AtomicLong)}.
248 * <p>
249 * There are several different startpoint types (see
250 * {@link SetStartpointOptions}), and this implementation is not required to
251 * support them all. If the specified startpoint type is unsupported, this
252 * method should throw an {@link IllegalArgumentException}.
253 * <p>
254 * <b>IMPORTANT</b>: The <code>RESUME_AT_SERIALIZABLE</code> startpoint type
255 * must be supported by your implementation, because this is used when a Sync
256 * Pipe first starts up.
257 * <p>
258 * This method can be called from two different contexts:
259 * <ul>
260 * <li>When the 'set-startpoint' subcommand of the realtime-sync CLI is used
261 * (the Sync Pipe is required to be stopped in this context)</li>
262 * <li>Immediately after a Sync Pipe starts up and a connection is first
263 * established to the source server (e.g. before the first call to
264 * {@link #getNextBatchOfChanges(TransactionContext, int, AtomicLong)})</li>
265 * </ul>
266 * <p>
267 * A {@link TransactionContext} is provided, which allows
268 * controlled access to the target database. The context will contain a fresh
269 * fresh connection (i.e. a new transaction), and the Synchronization Server
270 * will always commit or rollback the transaction automatically, depending on
271 * whether this method returns normally or throws an exception. Implementers
272 * may optionally perform their own transaction management within this method
273 * if necessary.
274 *
275 * @param ctx
276 * a TransactionContext which provides a valid JDBC connection to the
277 * database.
278 * @param options
279 * an object which indicates where exactly to start synchronizing
280 * (e.g.
281 * the end of the changelog, specific change number, a certain time
282 * ago, etc)
283 * @throws SQLException
284 * if there is any error while setting the start point
285 */
286 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
287 public abstract void setStartpoint(final TransactionContext ctx,
288 final SetStartpointOptions options)
289 throws SQLException;
290
291
292
293 /**
294 * Gets the current value of the startpoint for change detection. This is the
295 * "bookmark" which indicates which changes have already been processed and
296 * which have not. In most cases, a change number is used to detect changes
297 * and is managed by the Synchronization Server, in which case this
298 * implementation needs only to return the latest acknowledged
299 * change number. In other cases, the return value may correspond to a
300 * different value, such as the SYS_CHANGE_VERSION in Microsoft SQL Server.
301 * In any case, this method should return the value that is updated by
302 * {@link #acknowledgeCompletedOps(TransactionContext, LinkedList)}.
303 * <p>
304 * This method is called periodically and the return value is saved in the
305 * persistent state for the Sync Pipe that uses this script as its Sync
306 * Source.
307 * <p>
308 * <b>IMPORTANT</b>: The internal value for the startpoint should only be
309 * updated after a sync operation is acknowledged back to this script (via
310 * {@link #acknowledgeCompletedOps(TransactionContext, LinkedList)}).
311 * Otherwise it will be possible for changes to be missed when the
312 * Synchronization Server is restarted or a connection error occurs.
313 * @return a value to store in the persistent state for the Sync Pipe. This is
314 * usually
315 * a change number, but if a changelog table is not used to detect
316 * changes,
317 * this value should represent some other token to pass into
318 * {@link #setStartpoint(TransactionContext, SetStartpointOptions)}
319 * when the sync pipe starts up.
320 */
321 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
322 public abstract Serializable getStartpoint();
323
324
325
326 /**
327 * Return the next batch of change records from the database. Change records
328 * are just hints that a change happened; they do not include the actual data
329 * of the change. In an effort to never synchronize stale data, the
330 * Synchronization Server will go back and fetch the full source entry for
331 * each change record.
332 * <p>
333 * On the first invocation, this should return changes starting from the
334 * startpoint that was set by
335 * {@link #setStartpoint(TransactionContext, SetStartpointOptions)}. This
336 * method is responsible for updating the internal state such that subsequent
337 * invocations do not return duplicate changes.
338 * <p>
339 * The resulting list should be limited by <code>maxChanges</code>. The
340 * <code>numStillPending</code> reference should be set to the estimated
341 * number of changes that haven't yet been retrieved from the changelog table
342 * when this method returns, or zero if all the current changes have been
343 * retrieved.
344 * <p>
345 * <b>IMPORTANT</b>: While this method needs to keep track of which changes
346 * have already been returned so that it does not return them again, it should
347 * <b>NOT</b> modify the official startpoint. The internal value for the
348 * startpoint should only be updated after a sync operation is acknowledged
349 * back to this script (via
350 * {@link #acknowledgeCompletedOps(TransactionContext, LinkedList)}).
351 * Otherwise it will be possible for changes to be missed when the
352 * Synchronization Server is restarted or a connection error occurs. The
353 * startpoint should not change as a result of this method.
354 * <p>
355 * A {@link TransactionContext} is provided, which allows
356 * controlled access to the target database. The context will contain a fresh
357 * fresh connection (i.e. a new transaction), and the Synchronization Server
358 * will always commit or rollback the transaction automatically, depending on
359 * whether this method returns normally or throws an exception. Implementers
360 * may optionally perform their own transaction management within this method
361 * if necessary.
362 * <p>
363 * This method <b>does not need to be thread-safe</b>. It will be invoked
364 * repeatedly by a single thread, based on the polling interval set in the
365 * Sync Pipe configuration.
366 * @param ctx
367 * a TransactionContext which provides a valid JDBC connection to the
368 * database.
369 * @param maxChanges
370 * the maximum number of changes to retrieve
371 * @param numStillPending
372 * this should be set to the number of unretrieved changes that
373 * are still pending after this batch has been retrieved. This will
374 * be passed in
375 * as zero, and may be left that way if the actual value cannot be
376 * determined.
377 * @return a list of {@link DatabaseChangeRecord} instances, each
378 * corresponding
379 * to a row in the changelog table (or the equivalent if some other
380 * change
381 * tracking mechanism is being used). If there are no new changes to
382 * return, this
383 * method should return an empty list.
384 * @throws SQLException
385 * if there is any error while retrieving the next batch of changes
386 */
387 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
388 public abstract List<DatabaseChangeRecord> getNextBatchOfChanges(
389 final TransactionContext ctx,
390 final int maxChanges,
391 final AtomicLong numStillPending)
392 throws SQLException;
393
394
395
396 /**
397 * Return a full source entry (in LDAP form) from the database, corresponding
398 * to the {@link DatabaseChangeRecord} that is passed in through the
399 * {@link SyncOperation}. This method should perform any queries necessary to
400 * gather the latest values for all the attributes to be synchronized.
401 * <p>
402 * A {@link TransactionContext} is provided, which allows
403 * controlled access to the target database. The context will contain a fresh
404 * fresh connection (i.e. a new transaction), and the Synchronization Server
405 * will always commit or rollback the transaction automatically, depending on
406 * whether this method returns normally or throws an exception. Implementers
407 * may optionally perform their own transaction management within this method
408 * if necessary.
409 * <p>
410 * This method <b>must be thread safe</b>, as it will be called repeatedly and
411 * concurrently by each of the Sync Pipe worker threads as they process
412 * entries.
413 * @param ctx
414 * a TransactionContext which provides a valid JDBC connection to the
415 * database.
416 * @param operation
417 * the SyncOperation which identifies the database "entry" to
418 * fetch. The DatabaseChangeRecord can be obtained by calling
419 * <code>operation.getDatabaseChangeRecord()</code>.
420 * This is returned by
421 * {@link #getNextBatchOfChanges(TransactionContext, int, AtomicLong)}
422 * or by
423 * {@link #listAllEntries(TransactionContext, String, BlockingQueue)}
424 * .
425 * @return a full LDAP Entry, or null if no such entry exists.
426 * @throws SQLException
427 * if there is an error fetching the entry
428 */
429 @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
430 public abstract Entry fetchEntry(final TransactionContext ctx,
431 final SyncOperation operation)
432 throws SQLException;
433
434
435
436 /**
437 * Provides a way for the Synchronization Server to acknowledge back to the
438 * script which sync operations it has processed. This method should update
439 * the official startpoint which was set by
440 * {@link #setStartpoint(TransactionContext, SetStartpointOptions)} and is
441 * returned by {@link #getStartpoint()}.
442 * <p>
443 * <b>IMPORTANT</b>: The internal value for the startpoint should only be
444 * updated after a sync operation is acknowledged back to this script (via
445 * this method). Otherwise it will be possible for changes to be missed when
446 * the Synchronization Server is restarted or a connection error occurs.
447 * <p>
448 * A {@link TransactionContext} is provided in case the acknowledgment needs
449 * to make it all the way back to the database itself (for example if you were
450 * using Oracle's Change Data Capture). The context will contain a fresh
451 * fresh connection (i.e. a new transaction), and the Synchronization Server
452 * will always commit or rollback the transaction automatically, depending on
453 * whether this method returns normally or throws an exception. Implementers
454 * may optionally perform their own transaction management within this method
455 * if necessary.
456 *
457 * @param ctx
458 * a TransactionContext which provides a valid JDBC connection to the
459 * database.
460 * @param completedOps
461 * a list of {@link SyncOperation}s that have finished processing.
462 * The records are listed in the order they were first detected.
463 * @throws SQLException
464 * if there is an error acknowledging the changes back to the
465 * database
466 */
467 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
468 public abstract void acknowledgeCompletedOps(
469 final TransactionContext ctx,
470 final LinkedList<SyncOperation> completedOps)
471 throws SQLException;
472
473
474
475 /**
476 * Performs a cleanup of the changelog table (if desired). There is a
477 * background thread that periodically invokes this method. It should remove
478 * any rows in the changelog table that are more than
479 * <code>maxAgeMillis</code> milliseconds old.
480 * <p>
481 * <b>NOTE:</b> If the system clock on the database server is not in sync with
482 * the system clock on the Synchronization Server, this method should query
483 * the database for its current time in order to determine the cut-off point
484 * for deleting changelog records.
485 * <p>
486 * A {@link TransactionContext} is provided, which allows
487 * controlled access to the target database. The context will contain a fresh
488 * fresh connection (i.e. a new transaction), and the Synchronization Server
489 * will always commit or rollback the transaction automatically, depending on
490 * whether this method returns normally or throws an exception. Implementers
491 * may optionally perform their own transaction management within this method
492 * if necessary.
493 * <p>
494 * If a separate mechanism will be used to manage the changelog table, this
495 * method may be implemented as a no-op and always return zero. This is how
496 * the default implementation behaves.
497 * @param ctx
498 * a TransactionContext which provides a valid JDBC connection to the
499 * database.
500 * @param maxAgeMillis
501 * the period of time (in milliseconds) after which a changelog table
502 * record should be deleted
503 * @return the number of rows that were deleted from the changelog table
504 * @throws SQLException
505 * if there is an error purging records from the changelog table
506 */
507 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
508 public int cleanupChangelog(final TransactionContext ctx,
509 final long maxAgeMillis)
510 throws SQLException
511 {
512 //no implementation provided by default; this is an opt-in feature.
513 return 0;
514 }
515
516
517
518 /**
519 * Gets a list of all the entries in the database for a given entry type. This
520 * is used by the 'resync' command line tool. The default implementation
521 * throws a {@link UnsupportedOperationException}; subclasses should override
522 * if the resync functionality is needed.
523 * <p>
524 * The <code>entryType</code> is user-defined; it will be
525 * passed in on the command line for resync. The <code>outputQueue</code>
526 * should contain {@link DatabaseChangeRecord} objects with the
527 * <code>ChangeType</code> set to <i>resync</i>.
528 * <p>
529 * This method should not return until all the entries of the given entryType
530 * have been added to the output queue. Separate threads will concurrently
531 * drain entries from the queue and process them. The queue should not
532 * actually contain full entries, but rather DatabaseChangeRecord objects
533 * which identify the full database entries. These objects are then
534 * individually passed in to
535 * {@link #fetchEntry(TransactionContext, SyncOperation)}. Therefore,
536 * it is important to make sure that the DatabaseChangeRecord instances
537 * contain enough identifiable information (e.g. primary keys) for each entry
538 * so that the entry can be found again.
539 * <p>
540 * The lifecycle of resync is similar to that of real-time sync, with a few
541 * differences:
542 * <ol>
543 * <li>Stream out a list of all IDs in the database (for a given entryType)
544 * </li>
545 * <li>Fetch full source entry for an ID</li>
546 * <li>Perform any mappings and compute the equivalent destination entry</li>
547 * <li>Fetch full destination entry</li>
548 * <li>Diff the computed destination entry and actual destination entry</li>
549 * <li>Apply the minimal set of changes at the destination to bring it in sync
550 * </li>
551 * </ol>
552 * If the total set of entries is very large, it is fine to split up the work
553 * into multiple database queries within this method. The queue will not grow
554 * out of control because it blocks when it becomes full. The queue capacity
555 * is fixed at 1000.
556 * <p>
557 * A {@link TransactionContext} is provided, which allows
558 * controlled access to the target database. The context will contain a fresh
559 * fresh connection (i.e. a new transaction), and the Synchronization Server
560 * will always commit or rollback the transaction automatically, depending on
561 * whether this method returns normally or throws an exception. Implementers
562 * may optionally perform their own transaction management within this method
563 * if necessary.
564 *
565 * @param ctx
566 * a TransactionContext which provides a valid JDBC connection to the
567 * database.
568 * @param entryType
569 * the type of database entry to be fetched (this is specified
570 * on the CLI for the resync command)
571 * @param outputQueue
572 * a queue of DatabaseChangeRecord objects which will be individually
573 * fetched via {@link #fetchEntry(TransactionContext, SyncOperation)}
574 * @throws SQLException
575 * if there is an error retrieving the list of entries to resync
576 */
577 @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
578 public void listAllEntries(final TransactionContext ctx,
579 final String entryType,
580 final BlockingQueue<DatabaseChangeRecord>
581 outputQueue) throws SQLException
582 {
583 throw new UnsupportedOperationException();
584 }
585
586
587
588 /**
589 * Note: This method is deprecated and may be removed in a future release.
590 * All new and existing code should be changed to use the version of this
591 * method which includes the <code>entryType</code> parameter.
592 * <p>
593 * Gets a list of all the entries in the database from a given file input.
594 * This is used by the 'resync' command line tool. The default implementation
595 * throws a {@link UnsupportedOperationException}; subclasses should override
596 * if the resync functionality is needed for specific database records, which
597 * can be specified in the input file.
598 * <p>
599 * The format for the <code>inputLines</code> (e.g. the content of the file)
600 * is user-defined; it may be key/value pairs, primary keys, or full SQL
601 * statements, for example. The use of this method is triggered via the
602 * <i>--sourceInputFile</i> argument on the resync CLI. The
603 * <code>outputQueue</code> should contain {@link DatabaseChangeRecord}
604 * objects with the <code>ChangeType</code> set to <i>resync</i>.
605 * <p>
606 * This method should not return until all the entries specified by the input
607 * file have been added to the output queue. Separate threads will
608 * concurrently drain entries from the queue and process them. The queue
609 * should not actually contain full entries, but rather DatabaseChangeRecord
610 * objects which identify the full database entries. These objects are then
611 * individually passed in to
612 * {@link #fetchEntry(TransactionContext, SyncOperation)}. Therefore,
613 * it is important to make sure that the DatabaseChangeRecord instances
614 * contain enough identifiable information (e.g. primary keys) for each entry
615 * so that the entry can be found again.
616 * <p>
617 * The lifecycle of resync is similar to that of real-time sync, with a few
618 * differences:
619 * <ol>
620 * <li>Stream out a list of all IDs in the database (using the given input
621 * file)</li>
622 * <li>Fetch full source entry for an ID</li>
623 * <li>Perform any mappings and compute the equivalent destination entry</li>
624 * <li>Fetch full destination entry</li>
625 * <li>Diff the computed destination entry and actual destination entry</li>
626 * <li>Apply the minimal set of changes at the destination to bring it in sync
627 * </li>
628 * </ol>
629 * If the total set of entries is very large, it is fine to split up the work
630 * into multiple database queries within this method. The queue will not grow
631 * out of control because it blocks when it becomes full. The queue capacity
632 * is fixed at 1000.
633 * <p>
634 * A {@link TransactionContext} is provided, which allows
635 * controlled access to the target database. The context will contain a fresh
636 * fresh connection (i.e. a new transaction), and the Synchronization Server
637 * will always commit or rollback the transaction automatically, depending on
638 * whether this method returns normally or throws an exception. Implementers
639 * may optionally perform their own transaction management within this method
640 * if necessary.
641 *
642 * @param ctx
643 * a TransactionContext which provides a valid JDBC connection to the
644 * database.
645 * @param inputLines
646 * an Iterator containing the lines from the specified input file to
647 * resync (this is specified on the CLI for the resync command).
648 * These lines can be any format, for example a set of primary keys,
649 * a set of WHERE clauses, a set of full SQL queries, etc.
650 * @param outputQueue
651 * a queue of DatabaseChangeRecord objects which will be individually
652 * fetched via {@link #fetchEntry(TransactionContext, SyncOperation)}
653 * @throws SQLException
654 * if there is an error retrieving the list of entries to resync
655 */
656 @Deprecated()
657 public void listAllEntries(final TransactionContext ctx,
658 final Iterator<String> inputLines,
659 final BlockingQueue<DatabaseChangeRecord>
660 outputQueue) throws SQLException
661 {
662 throw new UnsupportedOperationException();
663 }
664
665
666 /**
667 * Gets a list of all the entries in the database from a given file input.
668 * This is used by the 'resync' command line tool. The default implementation
669 * throws a {@link UnsupportedOperationException}; subclasses should override
670 * if the resync functionality is needed for specific database records, which
671 * can be specified in the input file.
672 * <p>
673 * The format for the <code>inputLines</code> (e.g. the content of the file)
674 * is user-defined; it may be key/value pairs, primary keys, or full SQL
675 * statements, for example. The use of this method is triggered via the
676 * <i>--sourceInputFile</i> argument on the resync CLI. The
677 * <code>outputQueue</code> should contain {@link DatabaseChangeRecord}
678 * objects with the <code>ChangeType</code> set to <i>resync</i>.
679 * <p>
680 * This method should not return until all the entries specified by the input
681 * file have been added to the output queue. Separate threads will
682 * concurrently drain entries from the queue and process them. The queue
683 * should not actually contain full entries, but rather DatabaseChangeRecord
684 * objects which identify the full database entries. These objects are then
685 * individually passed in to
686 * {@link #fetchEntry(TransactionContext, SyncOperation)}. Therefore,
687 * it is important to make sure that the DatabaseChangeRecord instances
688 * contain enough identifiable information (e.g. primary keys) for each entry
689 * so that the entry can be found again.
690 * <p>
691 * The lifecycle of resync is similar to that of real-time sync, with a few
692 * differences:
693 * <ol>
694 * <li>Stream out a list of all IDs in the database (using the given input
695 * file)</li>
696 * <li>Fetch full source entry for an ID</li>
697 * <li>Perform any mappings and compute the equivalent destination entry</li>
698 * <li>Fetch full destination entry</li>
699 * <li>Diff the computed destination entry and actual destination entry</li>
700 * <li>Apply the minimal set of changes at the destination to bring it in sync
701 * </li>
702 * </ol>
703 * If the total set of entries is very large, it is fine to split up the work
704 * into multiple database queries within this method. The queue will not grow
705 * out of control because it blocks when it becomes full. The queue capacity
706 * is fixed at 1000.
707 * <p>
708 * A {@link TransactionContext} is provided, which allows
709 * controlled access to the target database. The context will contain a fresh
710 * fresh connection (i.e. a new transaction), and the Synchronization Server
711 * will always commit or rollback the transaction automatically, depending on
712 * whether this method returns normally or throws an exception. Implementers
713 * may optionally perform their own transaction management within this method
714 * if necessary.
715 *
716 * @param ctx
717 * a TransactionContext which provides a valid JDBC connection to the
718 * database.
719 * @param entryType
720 * the type of database entry to be fetched (this is specified
721 * on the CLI for the resync command)
722 * @param inputLines
723 * an Iterator containing the lines from the specified input file to
724 * resync (this is specified on the CLI for the resync command).
725 * These lines can be any format, for example a set of primary keys,
726 * a set of WHERE clauses, a set of full SQL queries, etc.
727 * @param outputQueue
728 * a queue of DatabaseChangeRecord objects which will be individually
729 * fetched via {@link #fetchEntry(TransactionContext, SyncOperation)}
730 * @throws SQLException
731 * if there is an error retrieving the list of entries to resync
732 */
733 public void listAllEntries(final TransactionContext ctx,
734 final String entryType,
735 final Iterator<String> inputLines,
736 final BlockingQueue<DatabaseChangeRecord>
737 outputQueue) throws SQLException
738 {
739 throw new UnsupportedOperationException();
740 }
741 }