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