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-2014 UnboundID Corp.
026 */
027 package com.unboundid.directory.sdk.sync.scripting;
028
029 import java.util.List;
030
031 import com.unboundid.directory.sdk.common.internal.Configurable;
032 import com.unboundid.directory.sdk.sync.config.SyncDestinationConfig;
033 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
034 import com.unboundid.directory.sdk.sync.types.EndpointException;
035 import com.unboundid.directory.sdk.sync.types.SyncOperation;
036 import com.unboundid.directory.sdk.sync.types.SyncServerContext;
037 import com.unboundid.ldap.sdk.Entry;
038 import com.unboundid.ldap.sdk.Modification;
039 import com.unboundid.util.Extensible;
040 import com.unboundid.util.ThreadSafety;
041 import com.unboundid.util.ThreadSafetyLevel;
042 import com.unboundid.util.args.ArgumentException;
043 import com.unboundid.util.args.ArgumentParser;
044
045 /**
046 * This class defines an API that must be implemented by scripted extensions
047 * that wish to push changes processed by the Synchronization Server to an
048 * arbitrary destination. This type of sync destination is generic and can
049 * support a wide range of endpoints. In addition, this type of sync destination
050 * supports one-way notifications, where the source and destination entries are
051 * never compared but instead changes are pushed straight through.
052 *
053 * <H2>Configuring Scripted Sync Destinations</H2>
054 * In order to configure a sync destination created using this API, use
055 * a command like:
056 * <PRE>
057 * dsconfig create-sync-destination \
058 * --sync-destination-name "<I>{name}</I>" \
059 * --type groovy-scripted \
060 * --set "script-class:<I>{class-name}</I>" \
061 * --set "script-argument:<I>{name=value}</I>"
062 * </PRE>
063 * where "<I>{name}</I>" is the name to use for the sync destination
064 * instance, "<I>{script-name}</I>" is the fully-qualified name of the Java
065 * class that extends
066 * {@code com.unboundid.directory.sdk.sync.scripting.ScriptedSyncDestination},
067 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
068 * provide to the sync destination. If multiple arguments should be
069 * provided to extension, then the
070 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
071 * provided multiple times.
072 *
073 * @see
074 * com.unboundid.directory.sdk.sync.api.SyncDestination
075 */
076 @Extensible()
077 @SynchronizationServerExtension(appliesToLocalContent=false,
078 appliesToSynchronizedContent=true)
079 @ThreadSafety(level = ThreadSafetyLevel.MOSTLY_THREADSAFE)
080 public abstract class ScriptedSyncDestination implements Configurable
081 {
082
083 /**
084 * {@inheritDoc}
085 */
086 public void defineConfigArguments(final ArgumentParser parser)
087 throws ArgumentException
088 {
089 // No arguments will be allowed by default.
090 }
091
092
093
094 /**
095 * Initializes this sync destination. This is called when a Sync Pipe
096 * first starts up, or when the <i>resync</i> process first starts up. Any
097 * initialization should be performed here. This method should generally store
098 * the {@link SyncServerContext} in a class member so that it can be used
099 * elsewhere in the implementation.
100 * <p>
101 * The default implementation is empty.
102 *
103 * @param serverContext A handle to the server context for the server in
104 * which this extension is running. Extensions should
105 * typically store this in a class member.
106 * @param config The general configuration for this object.
107 * @param parser The argument parser which has been initialized from
108 * the configuration for this sync destination.
109 * @throws EndpointException
110 * if a problem occurs while initializing this
111 * sync destination.
112 */
113 public void initializeSyncDestination(
114 final SyncServerContext serverContext,
115 final SyncDestinationConfig config,
116 final ArgumentParser parser)
117 throws EndpointException
118 {
119 // No initialization will be performed by default.
120 }
121
122
123
124 /**
125 * This hook is called when a Sync Pipe shuts down, or when the <i>resync</i>
126 * process shuts down. Any clean-up of this sync destination should be
127 * performed here.
128 * <p>
129 * The default implementation is empty.
130 */
131 public void finalizeSyncDestination()
132 {
133 // No implementation is performed by default.
134 }
135
136
137
138 /**
139 * Return the URL or path identifying the destination endpoint
140 * to which this extension is transmitting data. This is used for logging
141 * purposes only, so it could just be a server name or hostname and port, etc.
142 *
143 * @return the path to the destination endpoint
144 */
145 public abstract String getCurrentEndpointURL();
146
147
148
149 /**
150 * Return a full destination entry (in LDAP form) from the destination
151 * endpoint, corresponding to the the source {@link Entry} that is passed in.
152 * This method should perform any queries necessary to gather the latest
153 * values for all the attributes to be synchronized and return them in an
154 * Entry.
155 * <p>
156 * This method only needs to be implemented if the 'synchronization-mode' on
157 * the Sync Pipe is set to 'standard'. If it is set to 'notification', this
158 * method will never be called, and the pipe will pass changes straight
159 * through to one of {@link #createEntry}, {@link #modifyEntry}, or
160 * {@link #deleteEntry}.
161 * <p>
162 * Note that the if the source entry was renamed (see
163 * {@link SyncOperation#isModifyDN}), the
164 * <code>destEntryMappedFromSrc</code> will have the new DN; the old DN can
165 * be obtained by calling
166 * {@link SyncOperation#getDestinationEntryBeforeChange()} and getting the DN
167 * from there. This method should return the entry in its existing form
168 * (i.e. with the old DN, before it is changed).
169 * <p>
170 * This method <b>must be thread safe</b>, as it will be called repeatedly and
171 * concurrently by each of the Sync Pipe worker threads as they process
172 * entries.
173 * @param destEntryMappedFromSrc
174 * the LDAP entry which corresponds to the destination "entry" to
175 * fetch
176 * @param operation
177 * the sync operation for this change
178 * @return a list containing the full LDAP entries that matched this search
179 * (there may be more than one), or an empty list if no such entry
180 * exists
181 * @throws EndpointException
182 * if there is an error fetching the entry
183 */
184 public List<Entry> fetchEntry(final Entry destEntryMappedFromSrc,
185 final SyncOperation operation)
186 throws EndpointException
187 {
188 throw new UnsupportedOperationException(
189 "The fetchEntry() method must be implemented in the " +
190 "ScriptedSyncDestination if the Sync Pipe is " +
191 "running in standard mode (see 'synchronization-mode' " +
192 "in the Sync Pipe configuration).");
193 }
194
195
196
197 /**
198 * Creates a full destination "entry", corresponding to the the LDAP
199 * {@link Entry} that is passed in. This method is responsible for
200 * transforming the contents of the entry into the desired format and
201 * transmitting it to the target destination. It should perform any inserts or
202 * updates necessary to make sure the entry is fully created on the
203 * destination endpoint.
204 * <p>
205 * This method <b>must be thread safe</b>, as it will be called repeatedly and
206 * concurrently by the Sync Pipe worker threads as they process CREATE
207 * operations.
208 * @param entryToCreate
209 * the LDAP entry which corresponds to the destination
210 * "entry" to create
211 * @param operation
212 * the sync operation for this change
213 * @throws EndpointException
214 * if there is an error creating the entry
215 */
216 public abstract void createEntry(final Entry entryToCreate,
217 final SyncOperation operation)
218 throws EndpointException;
219
220
221
222 /**
223 * Modify an "entry" on the destination, corresponding to the the LDAP
224 * {@link Entry} that is passed in. This method is responsible for
225 * transforming the contents of the entry into the desired format and
226 * transmitting it to the target destination. It may perform multiple updates
227 * (including inserting or deleting other attributes) in order to fully
228 * synchronize the entire entry on the destination endpoint.
229 * <p>
230 * Note that the if the source entry was renamed (see
231 * {@link SyncOperation#isModifyDN}), the
232 * <code>fetchedDestEntry</code> will have the old DN; the new DN can
233 * be obtained by calling
234 * {@link SyncOperation#getDestinationEntryAfterChange()} and getting the DN
235 * from there.
236 * <p>
237 * This method <b>must be thread safe</b>, as it will be called repeatedly and
238 * concurrently by the Sync Pipe worker threads as they process MODIFY
239 * operations.
240 * @param entryToModify
241 * the LDAP entry which corresponds to the destination
242 * "entry" to modify. If the synchronization mode is 'standard',
243 * this will be the entry that was returned by {@link #fetchEntry};
244 * otherwise if the synchronization mode is 'notification', this
245 * will be the mapped destination entry.
246 * @param modsToApply
247 * a list of Modification objects which should be applied
248 * @param operation
249 * the sync operation for this change
250 * @throws EndpointException
251 * if there is an error modifying the entry
252 */
253 public abstract void modifyEntry(final Entry entryToModify,
254 final List<Modification> modsToApply,
255 final SyncOperation operation)
256 throws EndpointException;
257
258
259
260 /**
261 * Delete a full "entry" from the destination, corresponding to the the LDAP
262 * {@link Entry} that is passed in. This method may perform multiple deletes
263 * or updates if necessary to fully delete the entry from the destination
264 * endpoint.
265 * <p>
266 * This method <b>must be thread safe</b>, as it will be called repeatedly and
267 * concurrently by the Sync Pipe worker threads as they process DELETE
268 * operations.
269 * @param entryToDelete
270 * the LDAP entry which corresponds to the destination
271 * "entry" to delete. If the synchronization mode is 'standard',
272 * this will be the entry that was returned by {@link #fetchEntry};
273 * otherwise if the synchronization mode is 'notification', this
274 * will be the mapped destination entry.
275 * @param operation
276 * the sync operation for this change
277 * @throws EndpointException
278 * if there is an error deleting the entry
279 */
280 public abstract void deleteEntry(final Entry entryToDelete,
281 final SyncOperation operation)
282 throws EndpointException;
283
284 }