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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.task;
028
029import static org.opends.guitools.controlpanel.util.Utilities.*;
030import static org.opends.messages.AdminToolMessages.*;
031
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.HashSet;
035import java.util.List;
036import java.util.Set;
037import java.util.TreeSet;
038
039import javax.naming.ldap.InitialLdapContext;
040import javax.swing.SwingUtilities;
041
042import org.forgerock.i18n.LocalizableMessage;
043import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
044import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
045import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor;
046import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
047import org.opends.guitools.controlpanel.ui.ProgressDialog;
048import org.opends.guitools.controlpanel.util.ConfigReader;
049import org.opends.guitools.controlpanel.util.Utilities;
050import org.opends.server.admin.client.ManagementContext;
051import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
052import org.opends.server.admin.client.ldap.LDAPManagementContext;
053import org.opends.server.admin.std.client.BackendCfgClient;
054import org.opends.server.admin.std.client.LocalDBBackendCfgClient;
055import org.opends.server.admin.std.client.PluggableBackendCfgClient;
056import org.opends.server.admin.std.client.RootCfgClient;
057import org.opends.server.backends.jeb.RemoveOnceLocalDBBackendIsPluggable;
058import org.opends.server.core.DirectoryServer;
059import org.opends.server.types.DN;
060import org.opends.server.types.OpenDsException;
061
062/**
063 * The task that is launched when an index must be deleted.
064 */
065public class DeleteIndexTask extends Task
066{
067  private final Set<String> backendSet;
068  private final List<AbstractIndexDescriptor> indexesToDelete = new ArrayList<>();
069  private final List<AbstractIndexDescriptor> deletedIndexes = new ArrayList<>();
070
071  /**
072   * Constructor of the task.
073   *
074   * @param info
075   *          the control panel information.
076   * @param dlg
077   *          the progress dialog where the task progress will be displayed.
078   * @param indexesToDelete
079   *          the indexes that must be deleted.
080   */
081  public DeleteIndexTask(ControlPanelInfo info, ProgressDialog dlg, List<AbstractIndexDescriptor> indexesToDelete)
082  {
083    super(info, dlg);
084    backendSet = new HashSet<>();
085    for (final AbstractIndexDescriptor index : indexesToDelete)
086    {
087      backendSet.add(index.getBackend().getBackendID());
088    }
089    this.indexesToDelete.addAll(indexesToDelete);
090  }
091
092  @Override
093  public Type getType()
094  {
095    return Type.DELETE_INDEX;
096  }
097
098  @Override
099  public Set<String> getBackends()
100  {
101    return backendSet;
102  }
103
104  @Override
105  public LocalizableMessage getTaskDescription()
106  {
107    if (backendSet.size() == 1)
108    {
109      return INFO_CTRL_PANEL_DELETE_INDEX_TASK_DESCRIPTION.get(getStringFromCollection(backendSet, ", "));
110    }
111    else
112    {
113      return INFO_CTRL_PANEL_DELETE_INDEX_IN_BACKENDS_TASK_DESCRIPTION.get(getStringFromCollection(backendSet, ", "));
114    }
115  }
116
117  @Override
118  public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons)
119  {
120    boolean canLaunch = true;
121    if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
122    {
123      // All the operations are incompatible if they apply to this
124      // backend for safety.  This is a short operation so the limitation
125      // has not a lot of impact.
126      final Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
127      backends.retainAll(getBackends());
128      if (!backends.isEmpty())
129      {
130        incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
131        canLaunch = false;
132      }
133    }
134    return canLaunch;
135  }
136
137  /**
138   * Update the configuration in the server.
139   *
140   * @throws OpenDsException
141   *           if an error occurs.
142   */
143  private void updateConfiguration() throws OpenDsException
144  {
145    boolean configHandlerUpdated = false;
146    final int totalNumber = indexesToDelete.size();
147    int numberDeleted = 0;
148    try
149    {
150      if (!isServerRunning())
151      {
152        configHandlerUpdated = true;
153        getInfo().stopPooling();
154        if (getInfo().mustDeregisterConfig())
155        {
156          DirectoryServer.deregisterBaseDN(DN.valueOf("cn=config"));
157        }
158        DirectoryServer.getInstance().initializeConfiguration(
159            org.opends.server.extensions.ConfigFileHandler.class.getName(), ConfigReader.configFile);
160        getInfo().setMustDeregisterConfig(true);
161      }
162      boolean isFirst = true;
163      for (final AbstractIndexDescriptor index : indexesToDelete)
164      {
165        if (!isFirst)
166        {
167          SwingUtilities.invokeLater(new Runnable()
168          {
169            @Override
170            public void run()
171            {
172              getProgressDialog().appendProgressHtml("<br><br>");
173            }
174          });
175        }
176        isFirst = false;
177        if (isServerRunning())
178        {
179          SwingUtilities.invokeLater(new Runnable()
180          {
181            @Override
182            public void run()
183            {
184              final List<String> args = getObfuscatedCommandLineArguments(getDSConfigCommandLineArguments(index));
185              args.removeAll(getConfigCommandLineArguments());
186              printEquivalentCommandLine(getConfigCommandLineName(index), args,
187                  INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_INDEX.get());
188            }
189          });
190        }
191        SwingUtilities.invokeLater(new Runnable()
192        {
193          @Override
194          public void run()
195          {
196            if (isVLVIndex(index))
197            {
198              getProgressDialog().appendProgressHtml(
199                  Utilities.getProgressWithPoints(INFO_CTRL_PANEL_DELETING_VLV_INDEX.get(index.getName()),
200                      ColorAndFontConstants.progressFont));
201            }
202            else
203            {
204              getProgressDialog().appendProgressHtml(
205                  Utilities.getProgressWithPoints(INFO_CTRL_PANEL_DELETING_INDEX.get(index.getName()),
206                      ColorAndFontConstants.progressFont));
207            }
208          }
209        });
210        if (isServerRunning())
211        {
212          deleteIndex(getInfo().getDirContext(), index);
213        }
214        else
215        {
216          deleteIndex(index);
217        }
218        numberDeleted++;
219        final int fNumberDeleted = numberDeleted;
220        SwingUtilities.invokeLater(new Runnable()
221        {
222          @Override
223          public void run()
224          {
225            getProgressDialog().getProgressBar().setIndeterminate(false);
226            getProgressDialog().getProgressBar().setValue((fNumberDeleted * 100) / totalNumber);
227            getProgressDialog().appendProgressHtml(Utilities.getProgressDone(ColorAndFontConstants.progressFont));
228          }
229        });
230        deletedIndexes.add(index);
231      }
232    }
233    finally
234    {
235      if (configHandlerUpdated)
236      {
237        DirectoryServer.getInstance().initializeConfiguration(ConfigReader.configClassName, ConfigReader.configFile);
238        getInfo().startPooling();
239      }
240    }
241  }
242
243  /**
244   * Returns <CODE>true</CODE> if the index is a VLV index and
245   * <CODE>false</CODE> otherwise.
246   *
247   * @param index
248   *          the index.
249   * @return <CODE>true</CODE> if the index is a VLV index and
250   *         <CODE>false</CODE> otherwise.
251   */
252  private boolean isVLVIndex(AbstractIndexDescriptor index)
253  {
254    return index instanceof VLVIndexDescriptor;
255  }
256
257  /**
258   * Deletes an index. The code assumes that the server is not running and that
259   * the configuration file can be edited.
260   *
261   * @param index
262   *          the index to be deleted.
263   * @throws OpenDsException
264   *           if an error occurs.
265   */
266  private void deleteIndex(AbstractIndexDescriptor index) throws OpenDsException
267  {
268    final String backendId = Utilities.getRDNString("ds-cfg-backend-id", index.getBackend().getBackendID());
269    String dn;
270    if (isVLVIndex(index))
271    {
272      dn = getRDNString("ds-cfg-name", index.getName()) + ",cn=VLV Index," + backendId + ",cn=Backends,cn=config";
273    }
274    else
275    {
276      dn = getRDNString("ds-cfg-attribute", index.getName()) + ",cn=Index," + backendId + ",cn=Backends,cn=config";
277    }
278    DirectoryServer.getConfigHandler().deleteEntry(DN.valueOf(dn), null);
279  }
280
281  /**
282   * Deletes an index. The code assumes that the server is running and that the
283   * provided connection is active.
284   *
285   * @param index
286   *          the index to be deleted.
287   * @param ctx
288   *          the connection to the server.
289   * @throws OpenDsException
290   *           if an error occurs.
291   */
292  private void deleteIndex(final InitialLdapContext ctx, final AbstractIndexDescriptor index) throws OpenDsException
293  {
294    final ManagementContext mCtx = LDAPManagementContext.createFromContext(JNDIDirContextAdaptor.adapt(ctx));
295    final RootCfgClient root = mCtx.getRootConfiguration();
296    final BackendCfgClient backend = root.getBackend(index.getBackend().getBackendID());
297
298    if (backend instanceof LocalDBBackendCfgClient)
299    {
300      removeLocalDBIndex((LocalDBBackendCfgClient) backend, index);
301    }
302    else
303    {
304      removeBackendIndex((PluggableBackendCfgClient) backend, index);
305    }
306    backend.commit();
307  }
308
309  private void removeBackendIndex(final PluggableBackendCfgClient backend, final AbstractIndexDescriptor index)
310      throws OpenDsException
311  {
312    final String indexName = index.getName();
313    if (isVLVIndex(index))
314    {
315      backend.removeBackendVLVIndex(indexName);
316    }
317    else
318    {
319      backend.removeBackendIndex(indexName);
320    }
321  }
322
323  @RemoveOnceLocalDBBackendIsPluggable
324  private void removeLocalDBIndex(final LocalDBBackendCfgClient backend, final AbstractIndexDescriptor index)
325      throws OpenDsException
326  {
327    final String indexName = index.getName();
328    if (isVLVIndex(index))
329    {
330      backend.removeLocalDBVLVIndex(indexName);
331    }
332    else
333    {
334      backend.removeLocalDBIndex(indexName);
335    }
336  }
337
338  @Override
339  protected String getCommandLinePath()
340  {
341    return null;
342  }
343
344  @Override
345  protected ArrayList<String> getCommandLineArguments()
346  {
347    return new ArrayList<>();
348  }
349
350  /**
351   * Returns the path of the command line to be used to delete the specified
352   * index.
353   *
354   * @param index
355   *          the index to be deleted.
356   * @return the path of the command line to be used to delete the specified
357   *         index.
358   */
359  private String getConfigCommandLineName(AbstractIndexDescriptor index)
360  {
361    if (isServerRunning())
362    {
363      return getCommandLinePath("dsconfig");
364    }
365    else
366    {
367      return null;
368    }
369  }
370
371  @Override
372  public void runTask()
373  {
374    state = State.RUNNING;
375    lastException = null;
376
377    try
378    {
379      updateConfiguration();
380      state = State.FINISHED_SUCCESSFULLY;
381    }
382    catch (final Throwable t)
383    {
384      lastException = t;
385      state = State.FINISHED_WITH_ERROR;
386    }
387    finally
388    {
389      for (final AbstractIndexDescriptor index : deletedIndexes)
390      {
391        getInfo().unregisterModifiedIndex(index);
392      }
393    }
394  }
395
396  /**
397   * Return the dsconfig arguments required to delete an index.
398   *
399   * @param index
400   *          the index to be deleted.
401   * @return the dsconfig arguments required to delete an index.
402   */
403  private List<String> getDSConfigCommandLineArguments(AbstractIndexDescriptor index)
404  {
405    final List<String> args = new ArrayList<>();
406    if (isVLVIndex(index))
407    {
408      args.add("delete-local-db-vlv-index");
409    }
410    else
411    {
412      args.add("delete-local-db-index");
413    }
414    args.add("--backend-name");
415    args.add(index.getBackend().getBackendID());
416
417    args.add("--index-name");
418    args.add(index.getName());
419
420    args.addAll(getConnectionCommandLineArguments());
421    args.add("--no-prompt");
422    args.add(getNoPropertiesFileArgument());
423
424    return args;
425  }
426}