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