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