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