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