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