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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.workflowelement.localbackend;
028
029
030
031 import java.util.ArrayList;
032 import java.util.List;
033 import java.util.TreeMap;
034
035 import org.opends.messages.Message;
036 import org.opends.server.admin.server.ConfigurationChangeListener;
037 import org.opends.server.admin.std.server.LocalBackendWorkflowElementCfg;
038 import org.opends.server.api.Backend;
039 import org.opends.server.config.ConfigException;
040 import org.opends.server.core.AddOperation;
041 import org.opends.server.core.BindOperation;
042 import org.opends.server.core.CompareOperation;
043 import org.opends.server.core.DeleteOperation;
044 import org.opends.server.core.DirectoryServer;
045 import org.opends.server.core.ModifyDNOperation;
046 import org.opends.server.core.ModifyOperation;
047 import org.opends.server.core.SearchOperation;
048 import org.opends.server.types.*;
049 import org.opends.server.workflowelement.LeafWorkflowElement;
050
051
052
053 /**
054 * This class defines a local backend workflow element; e-g an entity that
055 * handle the processing of an operation aginst a local backend.
056 */
057 public class LocalBackendWorkflowElement extends
058 LeafWorkflowElement<LocalBackendWorkflowElementCfg>
059 implements ConfigurationChangeListener<LocalBackendWorkflowElementCfg>
060 {
061 // the backend associated with the local workflow element
062 private Backend backend;
063
064
065 // the set of local backend workflow elements registered with the server
066 private static TreeMap<String, LocalBackendWorkflowElement>
067 registeredLocalBackends =
068 new TreeMap<String, LocalBackendWorkflowElement>();
069
070
071 // a lock to guarantee safe concurrent access to the registeredLocalBackends
072 // variable
073 private static Object registeredLocalBackendsLock = new Object();
074
075
076 /**
077 * Creates a new instance of the local backend workflow element.
078 */
079 public LocalBackendWorkflowElement()
080 {
081 // There is nothing to do in this constructor.
082 }
083
084
085 /**
086 * Initializes a new instance of the local backend workflow element.
087 * This method is intended to be called by DirectoryServer when
088 * workflow configuration mode is auto as opposed to
089 * initializeWorkflowElement which is invoked when workflow
090 * configuration mode is manual.
091 *
092 * @param workflowElementID the workflow element identifier
093 * @param backend the backend associated to that workflow element
094 */
095 private void initialize(String workflowElementID, Backend backend)
096 {
097 // Initialize the workflow ID
098 super.initialize(workflowElementID);
099
100 this.backend = backend;
101
102 if (this.backend != null)
103 {
104 setPrivate(this.backend.isPrivateBackend());
105 }
106 }
107
108
109 /**
110 * Initializes a new instance of the local backend workflow element.
111 * This method is intended to be called by DirectoryServer when
112 * workflow configuration mode is manual as opposed to
113 * initialize(String,Backend) which is invoked when workflow
114 * configuration mode is auto.
115 *
116 * @param configuration The configuration for this local backend
117 * workflow element.
118 *
119 * @throws ConfigException If there is a problem with the provided
120 * configuration.
121 *
122 * @throws InitializationException If an error occurs while trying
123 * to initialize this workflow
124 * element that is not related to
125 * the provided configuration.
126 */
127 public void initializeWorkflowElement(
128 LocalBackendWorkflowElementCfg configuration
129 ) throws ConfigException, InitializationException
130 {
131 configuration.addLocalBackendChangeListener(this);
132
133 // Read configuration and apply changes.
134 processWorkflowElementConfig(configuration, true);
135 }
136
137
138 /**
139 * {@inheritDoc}
140 */
141 public void finalizeWorkflowElement()
142 {
143 // null all fields so that any use of the finalized object will raise
144 // an NPE
145 super.initialize(null);
146 backend = null;
147 }
148
149
150 /**
151 * {@inheritDoc}
152 */
153 public boolean isConfigurationChangeAcceptable(
154 LocalBackendWorkflowElementCfg configuration,
155 List<Message> unacceptableReasons
156 )
157 {
158 boolean isAcceptable =
159 processWorkflowElementConfig(configuration, false);
160
161 return isAcceptable;
162 }
163
164
165 /**
166 * {@inheritDoc}
167 */
168 public ConfigChangeResult applyConfigurationChange(
169 LocalBackendWorkflowElementCfg configuration
170 )
171 {
172 // Returned result.
173 ConfigChangeResult changeResult = new ConfigChangeResult(
174 ResultCode.SUCCESS, false, new ArrayList<Message>()
175 );
176
177 processWorkflowElementConfig(configuration, true);
178
179 return changeResult;
180 }
181
182
183 /**
184 * Parses the provided configuration and configure the workflow element.
185 *
186 * @param configuration The new configuration containing the changes.
187 * @param applyChanges If true then take into account the new configuration.
188 *
189 * @return <code>true</code> if the configuration is acceptable.
190 */
191 private boolean processWorkflowElementConfig(
192 LocalBackendWorkflowElementCfg configuration,
193 boolean applyChanges
194 )
195 {
196 // returned status
197 boolean isAcceptable = true;
198
199 // If the workflow element is disabled then do nothing. Note that the
200 // config manager could have finalized the object right before.
201 if (configuration.isEnabled())
202 {
203 // Read configuration.
204 String newBackendID = configuration.getBackend();
205 Backend newBackend = DirectoryServer.getBackend(newBackendID);
206
207 // Get the new config
208 if (applyChanges)
209 {
210 super.initialize(configuration.getWorkflowElementId());
211 backend = newBackend;
212 }
213 }
214
215 return isAcceptable;
216 }
217
218
219 /**
220 * Creates and registers a local backend with the server.
221 *
222 * @param workflowElementID the identifier of the workflow element to create
223 * @param backend the backend to associate with the local backend
224 * workflow element
225 *
226 * @return the existing local backend workflow element if it was
227 * already created or a newly created local backend workflow
228 * element.
229 */
230 public static LocalBackendWorkflowElement createAndRegister(
231 String workflowElementID,
232 Backend backend)
233 {
234 LocalBackendWorkflowElement localBackend = null;
235
236 // If the requested workflow element does not exist then create one.
237 localBackend = registeredLocalBackends.get(workflowElementID);
238 if (localBackend == null)
239 {
240 localBackend = new LocalBackendWorkflowElement();
241 localBackend.initialize(workflowElementID, backend);
242
243 // store the new local backend in the list of registered backends
244 registerLocalBackend(localBackend);
245 }
246
247 return localBackend;
248 }
249
250
251
252 /**
253 * Removes a local backend that was registered with the server.
254 *
255 * @param workflowElementID the identifier of the workflow element to remove
256 */
257 public static void remove(String workflowElementID)
258 {
259 deregisterLocalBackend(workflowElementID);
260 }
261
262
263
264 /**
265 * Removes all the local backends that were registered with the server.
266 * This function is intended to be called when the server is shutting down.
267 */
268 public static void removeAll()
269 {
270 synchronized (registeredLocalBackendsLock)
271 {
272 for (LocalBackendWorkflowElement localBackend:
273 registeredLocalBackends.values())
274 {
275 deregisterLocalBackend(localBackend.getWorkflowElementID());
276 }
277 }
278 }
279
280
281
282 /**
283 * Registers a local backend with the server.
284 *
285 * @param localBackend the local backend to register with the server
286 */
287 private static void registerLocalBackend(
288 LocalBackendWorkflowElement localBackend)
289 {
290 synchronized (registeredLocalBackendsLock)
291 {
292 String localBackendID = localBackend.getWorkflowElementID();
293 LocalBackendWorkflowElement existingLocalBackend =
294 registeredLocalBackends.get(localBackendID);
295
296 if (existingLocalBackend == null)
297 {
298 TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
299 new TreeMap
300 <String, LocalBackendWorkflowElement>(registeredLocalBackends);
301 newLocalBackends.put(localBackendID, localBackend);
302 registeredLocalBackends = newLocalBackends;
303 }
304 }
305 }
306
307
308
309 /**
310 * Deregisters a local backend with the server.
311 *
312 * @param workflowElementID the identifier of the workflow element to remove
313 */
314 private static void deregisterLocalBackend(String workflowElementID)
315 {
316 synchronized (registeredLocalBackendsLock)
317 {
318 LocalBackendWorkflowElement existingLocalBackend =
319 registeredLocalBackends.get(workflowElementID);
320
321 if (existingLocalBackend != null)
322 {
323 TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
324 new TreeMap<String, LocalBackendWorkflowElement>(
325 registeredLocalBackends);
326 newLocalBackends.remove(workflowElementID);
327 registeredLocalBackends = newLocalBackends;
328 }
329 }
330 }
331
332
333
334 /**
335 * {@inheritDoc}
336 */
337 public void execute(Operation operation) throws CanceledOperationException {
338 switch (operation.getOperationType())
339 {
340 case BIND:
341 LocalBackendBindOperation bindOperation =
342 new LocalBackendBindOperation((BindOperation) operation);
343 bindOperation.processLocalBind(backend);
344 break;
345
346 case SEARCH:
347 LocalBackendSearchOperation searchOperation =
348 new LocalBackendSearchOperation((SearchOperation) operation);
349 searchOperation.processLocalSearch(backend);
350 break;
351
352 case ADD:
353 LocalBackendAddOperation addOperation =
354 new LocalBackendAddOperation((AddOperation) operation);
355 addOperation.processLocalAdd(backend);
356 break;
357
358 case DELETE:
359 LocalBackendDeleteOperation deleteOperation =
360 new LocalBackendDeleteOperation((DeleteOperation) operation);
361 deleteOperation.processLocalDelete(backend);
362 break;
363
364 case MODIFY:
365 LocalBackendModifyOperation modifyOperation =
366 new LocalBackendModifyOperation((ModifyOperation) operation);
367 modifyOperation.processLocalModify(backend);
368 break;
369
370 case MODIFY_DN:
371 LocalBackendModifyDNOperation modifyDNOperation =
372 new LocalBackendModifyDNOperation((ModifyDNOperation) operation);
373 modifyDNOperation.processLocalModifyDN(backend);
374 break;
375
376 case COMPARE:
377 LocalBackendCompareOperation compareOperation =
378 new LocalBackendCompareOperation((CompareOperation) operation);
379 compareOperation.processLocalCompare(backend);
380 break;
381
382 case ABANDON:
383 // There is no processing for an abandon operation.
384 break;
385
386 default:
387 throw new AssertionError("Attempted to execute an invalid operation " +
388 "type: " + operation.getOperationType() +
389 " (" + operation + ")");
390 }
391 }
392
393
394
395 /**
396 * Attaches the current local operation to the global operation so that
397 * operation runner can execute local operation post response later on.
398 *
399 * @param <O> subtype of Operation
400 * @param <L> subtype of LocalBackendOperation
401 * @param globalOperation the global operation to which local operation
402 * should be attached to
403 * @param currentLocalOperation the local operation to attach to the global
404 * operation
405 */
406 @SuppressWarnings("unchecked")
407 public static final <O extends Operation,L> void
408 attachLocalOperation (O globalOperation, L currentLocalOperation)
409 {
410 List<?> existingAttachment =
411 (List<?>) globalOperation.getAttachment(Operation.LOCALBACKENDOPERATIONS);
412
413 List<L> newAttachment = new ArrayList<L>();
414
415 if (existingAttachment != null)
416 {
417 // This line raises an unchecked conversion warning.
418 // There is nothing we can do to prevent this warning
419 // so let's get rid of it since we know the cast is safe.
420 newAttachment.addAll ((List<L>) existingAttachment);
421 }
422 newAttachment.add (currentLocalOperation);
423 globalOperation.setAttachment(Operation.LOCALBACKENDOPERATIONS,
424 newAttachment);
425 }
426
427 }
428