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-2018 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.sync.api;
028
029
030
031import java.sql.SQLException;
032import java.util.Collections;
033import java.util.List;
034import java.util.Map;
035
036import com.unboundid.directory.sdk.common.internal.Configurable;
037import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039import com.unboundid.directory.sdk.sync.config.JDBCSyncDestinationConfig;
040import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041import com.unboundid.directory.sdk.sync.types.SyncOperation;
042import com.unboundid.directory.sdk.sync.types.SyncServerContext;
043import com.unboundid.directory.sdk.sync.types.TransactionContext;
044import com.unboundid.ldap.sdk.Entry;
045import com.unboundid.ldap.sdk.Modification;
046import com.unboundid.util.Extensible;
047import com.unboundid.util.ThreadSafety;
048import com.unboundid.util.ThreadSafetyLevel;
049import com.unboundid.util.args.ArgumentException;
050import 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 how the method returns. If a method call returns
079 * successfully, then the transaction will be committed. If an exception is
080 * thrown, then the transaction will be rolled back. In rare situations, it
081 * might be necessary for an implementation to perform its own commit or
082 * rollback of transactions by calling methods on {@link TransactionContext}.
083 * <p>
084 * Several of these methods throw {@link SQLException}, which should be used in
085 * the case of any database access error. For other types of errors, runtime
086 * exceptions may be used (IllegalStateException, NullPointerException, etc.).
087 * The Synchronization Server will automatically retry operations that fail, up
088 * to a configurable amount of attempts. The exception to this rule is if a
089 * SQLException is thrown with a SQL state string beginning with "08"; this
090 * indicates a connection error, and in this case the operation is retried
091 * indefinitely.  For these reasons implementers should refrain from handling
092 * or wrapping any {@link SQLException} and instead let it be handled by the
093 * calling code.
094 * <BR>
095 * <H2>Configuring JDBC Sync Destinations</H2>
096 * In order to configure a JDBC sync destination based on this API and
097 * written in Java, use a command like:
098 * <PRE>
099 *      dsconfig create-sync-destination \
100 *           --destination-name "<I>{destination-name}</I>" \
101 *           --type third-party-jdbc \
102 *           --set "server:{server-name}" \
103 *           --set "extension-class:<I>{class-name}</I>" \
104 *           --set "extension-argument:<I>{name=value}</I>"
105 * </PRE>
106 * where "<I>{destination-name}</I>" is the name to use for the JDBC sync
107 * destination instance, "<I>{server-name}</I>" is the name of the JDBC external
108 * server that will be used as the sync destination, "<I>{class-name}</I>" is
109 * the fully-qualified name of the Java class written using this API, and
110 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
111 * provide to the JDBC sync destination.  If multiple arguments should be
112 * provided to the JDBC sync destination, then the
113 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
114 * provided multiple times.
115 */
116@Extensible()
117@SynchronizationServerExtension(appliesToLocalContent=false,
118     appliesToSynchronizedContent=true)
119public abstract class JDBCSyncDestination implements UnboundIDExtension,
120                                                     Configurable,
121                                                     ExampleUsageProvider
122{
123  /**
124   * Creates a new instance of this JDBC Sync Destination.  All sync
125   * destination implementations must include a default constructor, but any
126   * initialization should generally be done in the
127   * {@link #initializeJDBCSyncDestination} method.
128   */
129  public JDBCSyncDestination()
130  {
131    //no implementation is required
132  }
133
134
135
136  /**
137   * {@inheritDoc}
138   */
139  public abstract String getExtensionName();
140
141
142
143  /**
144   * {@inheritDoc}
145   */
146  public abstract String[] getExtensionDescription();
147
148
149
150  /**
151   * {@inheritDoc}
152   */
153  public Map<List<String>,String> getExamplesArgumentSets()
154  {
155    return Collections.emptyMap();
156  }
157
158
159
160  /**
161   * {@inheritDoc}
162   */
163  public void defineConfigArguments(final ArgumentParser parser)
164         throws ArgumentException
165  {
166    // No arguments will be allowed by default.
167  }
168
169
170
171  /**
172   * This hook is called when a Sync Pipe first starts up, or when the
173   * <i>resync</i> process first starts up. Any initialization of this sync
174   * destination should be performed here. This method should generally store
175   * the {@link SyncServerContext} in a class
176   * member so that it can be used elsewhere in the implementation.
177   * <p>
178   * A {@link TransactionContext} is provided, which allows
179   * controlled access to the target database. The context will contain a fresh
180   * fresh connection (i.e. a new transaction), and the Synchronization Server
181   * will always commit or rollback the transaction automatically, depending on
182   * whether this method returns normally or throws an exception. See the class
183   * level documentation for warnings and additional details.
184   * <p>
185   * The default implementation is empty.
186   *
187   * @param ctx
188   *          a TransactionContext which provides a valid JDBC connection to the
189   *          database.
190   * @param  serverContext  A handle to the server context for the server in
191   *                        which this extension is running.
192   * @param  config         The general configuration for this sync destination.
193   * @param  parser         The argument parser which has been initialized from
194   *                        the configuration for this JDBC sync destination.
195   */
196  @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
197  public void initializeJDBCSyncDestination(
198                                         final TransactionContext ctx,
199                                         final SyncServerContext serverContext,
200                                         final JDBCSyncDestinationConfig config,
201                                         final ArgumentParser parser)
202  {
203    // No initialization will be performed by default.
204  }
205
206
207
208  /**
209   * This hook is called when a Sync Pipe shuts down, or when the <i>resync</i>
210   * process shuts down. Any clean-up of this sync destination should be
211   * performed here.
212   * <p>
213   * A {@link TransactionContext} is provided, which allows
214   * controlled access to the target database. The context will contain a fresh
215   * fresh connection (i.e. a new transaction), and the Synchronization Server
216   * will always commit or rollback the transaction automatically, depending on
217   * whether this method returns normally or throws an exception. See the class
218   * level documentation for warnings and additional details.
219   * <p>
220   * The default implementation is empty.
221   *
222   * @param ctx
223   *          a TransactionContext which provides a valid JDBC connection to the
224   *          database.
225   */
226  @ThreadSafety(level = ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
227  public void finalizeJDBCSyncDestination(
228                                      final TransactionContext ctx)
229  {
230    //No implementation required by default.
231  }
232
233
234
235  /**
236   * Return a full destination entry (in LDAP form) from the database,
237   * corresponding to the source {@link Entry} that is passed in.
238   * This method should perform any queries necessary to gather the latest
239   * values for all the attributes to be synchronized and return them in an
240   * Entry.
241   * <p>
242   * Note that the if the source entry was renamed (see
243   * {@link SyncOperation#isModifyDN}), the
244   * <code>destEntryMappedFromSrc</code> will have the new DN; the old DN can
245   * be obtained by calling
246   * {@link SyncOperation#getDestinationEntryBeforeChange()} and getting the DN
247   * from there. This method should return the entry in its existing form
248   * (i.e. with the old DN, before it is changed).
249   * <p>
250   * A {@link TransactionContext} is provided, which allows
251   * controlled access to the target database. The context will contain a fresh
252   * fresh connection (i.e. a new transaction), and the Synchronization Server
253   * will always commit or rollback the transaction automatically, depending on
254   * whether this method returns normally or throws an exception. See the class
255   * level documentation for warnings and additional details.
256   * <p>
257   * This method <b>must be thread safe</b>, as it will be called repeatedly and
258   * concurrently by each of the Sync Pipe worker threads as they process
259   * entries.
260   *
261   * @param ctx
262   *          a TransactionContext which provides a valid JDBC connection to the
263   *          database.
264   * @param destEntryMappedFromSrc
265   *          the LDAP entry which corresponds to the database "entry" to fetch
266   * @param  operation
267   *          the sync operation for this change
268   * @return a full LDAP Entry, or null if no such entry exists.
269   * @throws SQLException
270   *           if there is an error fetching the entry
271   */
272  @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
273  public abstract Entry fetchEntry(final TransactionContext ctx,
274                                   final Entry destEntryMappedFromSrc,
275                                   final SyncOperation operation)
276                                              throws SQLException;
277
278
279
280  /**
281   * Creates a full database "entry", corresponding to the LDAP
282   * {@link Entry} that is passed in. This method should perform any inserts and
283   * updates necessary to make sure the entry is fully created on the database.
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. See the class
290   * level documentation for warnings and additional details.
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 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. See the class
331   * level documentation for warnings and additional details.
332   * <p>
333   * This method <b>must be thread safe</b>, as it will be called repeatedly and
334   * concurrently by the Sync Pipe worker threads as they process MODIFY
335   * operations.
336   * @param ctx
337   *          a TransactionContext which provides a valid JDBC connection to the
338   *          database.
339   * @param fetchedDestEntry
340   *          the LDAP entry which corresponds to the database "entry" to modify
341   * @param modsToApply
342   *          a list of Modification objects which should be applied
343   * @param  operation
344   *          the sync operation for this change
345   * @throws SQLException
346   *           if there is an error modifying the entry
347   */
348  @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
349  public abstract void modifyEntry(final TransactionContext ctx,
350                                   final Entry fetchedDestEntry,
351                                   final List<Modification> modsToApply,
352                                   final SyncOperation operation)
353                                          throws SQLException;
354
355
356
357  /**
358   * Delete a full "entry" from the database, corresponding to the LDAP
359   * {@link Entry} that is passed in. This method may perform multiple deletes
360   * or updates if necessary to fully delete the entry from the database.
361   * <p>
362   * A {@link TransactionContext} is provided, which allows
363   * controlled access to the target database. The context will contain a fresh
364   * fresh connection (i.e. a new transaction), and the Synchronization Server
365   * will always commit or rollback the transaction automatically, depending on
366   * whether this method returns normally or throws an exception. See the class
367   * level documentation for warnings and additional details.
368   * <p>
369   * This method <b>must be thread safe</b>, as it will be called repeatedly and
370   * concurrently by the Sync Pipe worker threads as they process DELETE
371   * operations.
372   *
373   * @param ctx
374   *          a TransactionContext which provides a valid JDBC connection to the
375   *          database.
376   * @param fetchedDestEntry
377   *          the LDAP entry which corresponds to the database "entry" to delete
378   * @param  operation
379   *          the sync operation for this change
380   * @throws SQLException
381   *           if there is an error deleting the entry
382   */
383  @ThreadSafety(level = ThreadSafetyLevel.METHOD_THREADSAFE)
384  public abstract void deleteEntry(final TransactionContext ctx,
385                                   final Entry fetchedDestEntry,
386                                   final SyncOperation operation)
387                                        throws SQLException;
388}