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