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 */
027
028package org.opends.guitools.controlpanel.ui;
029
030import static org.opends.messages.AdminToolMessages.*;
031
032import java.awt.Component;
033import java.awt.GridBagConstraints;
034import java.awt.GridBagLayout;
035import java.awt.Insets;
036import java.awt.event.ActionEvent;
037import java.awt.event.ActionListener;
038import java.util.ArrayList;
039import java.util.Collection;
040import java.util.HashSet;
041import java.util.List;
042import java.util.Set;
043import java.util.SortedSet;
044import java.util.TreeSet;
045
046import javax.naming.ldap.InitialLdapContext;
047import javax.swing.Box;
048import javax.swing.JCheckBox;
049import javax.swing.JComponent;
050import javax.swing.JPanel;
051import javax.swing.JScrollPane;
052import javax.swing.SwingConstants;
053import javax.swing.SwingUtilities;
054import javax.swing.border.EmptyBorder;
055import javax.swing.event.DocumentEvent;
056import javax.swing.event.DocumentListener;
057
058import org.forgerock.i18n.LocalizableMessage;
059import org.forgerock.i18n.slf4j.LocalizedLogger;
060import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
061import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
062import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
063import org.opends.guitools.controlpanel.datamodel.IndexTypeDescriptor;
064import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
065import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
066import org.opends.guitools.controlpanel.event.ScrollPaneBorderListener;
067import org.opends.guitools.controlpanel.task.DeleteIndexTask;
068import org.opends.guitools.controlpanel.task.Task;
069import org.opends.guitools.controlpanel.util.ConfigReader;
070import org.opends.guitools.controlpanel.util.Utilities;
071import org.opends.server.admin.client.ManagementContext;
072import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
073import org.opends.server.admin.client.ldap.LDAPManagementContext;
074import org.opends.server.admin.std.client.BackendCfgClient;
075import org.opends.server.admin.std.client.BackendIndexCfgClient;
076import org.opends.server.admin.std.client.LocalDBBackendCfgClient;
077import org.opends.server.admin.std.client.LocalDBIndexCfgClient;
078import org.opends.server.admin.std.client.PluggableBackendCfgClient;
079import org.opends.server.backends.jeb.RemoveOnceLocalDBBackendIsPluggable;
080import org.opends.server.core.DirectoryServer;
081import org.opends.server.types.AttributeType;
082import org.opends.server.types.DN;
083import org.opends.server.types.OpenDsException;
084
085/**
086 * The panel that displays an existing index (it appears on the right of the
087 * 'Manage Indexes' dialog).
088 */
089public class IndexPanel extends AbstractIndexPanel
090{
091  private static final long serialVersionUID = 1439500626486823366L;
092  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
093
094  private IndexDescriptor index;
095  private ScrollPaneBorderListener scrollListener;
096
097  private boolean ignoreCheckSave;
098
099  private ModifyIndexTask newModifyTask;
100
101  /**
102   * Default constructor.
103   */
104  public IndexPanel()
105  {
106    super();
107    createLayout();
108  }
109
110  /**
111   * Creates the layout of the panel (but the contents are not populated here).
112   */
113  private void createLayout()
114  {
115    GridBagConstraints gbc = new GridBagConstraints();
116    JPanel p = new JPanel(new GridBagLayout());
117    p.setOpaque(false);
118    super.createBasicLayout(p, gbc, true);
119    p.setBorder(new EmptyBorder(10, 10, 10, 10));
120    gbc = new GridBagConstraints();
121    gbc.weightx = 1.0;
122    gbc.weighty = 1.0;
123    gbc.fill = GridBagConstraints.BOTH;
124    gbc.gridx = 0;
125    gbc.gridy = 0;
126    JScrollPane scroll = Utilities.createBorderLessScrollBar(p);
127    scrollListener =
128      ScrollPaneBorderListener.createBottomBorderListener(scroll);
129    add(scroll, gbc);
130
131    gbc.gridy ++;
132    gbc.gridx = 0;
133    gbc.weightx = 1.0;
134    gbc.insets.left = 0;
135    gbc.gridwidth = 2;
136    gbc.weighty = 0.0;
137    gbc.fill = GridBagConstraints.HORIZONTAL;
138
139    gbc.insets = new Insets(10, 10, 0, 10);
140    add(warning, gbc);
141    Utilities.setWarningLabel(warning, INDEX_MODIFIED);
142
143    gbc.gridy ++;
144    JPanel buttonPanel = new JPanel(new GridBagLayout());
145    buttonPanel.setOpaque(false);
146    gbc.insets = new Insets(10, 10, 10, 10);
147    add(buttonPanel, gbc);
148
149    gbc.insets = new Insets(0, 0, 0, 0);
150    gbc.gridy = 0;
151    gbc.gridx = 0;
152    gbc.weightx = 0.0;
153    gbc.gridwidth = 1;
154    deleteIndex.setOpaque(false);
155    gbc.insets.left = 0;
156    buttonPanel.add(deleteIndex, gbc);
157    deleteIndex.addActionListener(new ActionListener()
158    {
159      /** {@inheritDoc} */
160      public void actionPerformed(ActionEvent ev)
161      {
162        deleteIndex();
163      }
164    });
165    gbc.gridx = 2;
166    gbc.weightx = 1.0;
167    buttonPanel.add(Box.createHorizontalGlue(), gbc);
168    gbc.weightx = 0.0;
169    gbc.insets.left = 10;
170    saveChanges.setOpaque(false);
171    gbc.gridx = 3;
172    buttonPanel.add(saveChanges, gbc);
173    saveChanges.addActionListener(new ActionListener()
174    {
175      public void actionPerformed(ActionEvent ev)
176      {
177        saveIndex(false);
178      }
179    });
180
181    entryLimit.getDocument().addDocumentListener(new DocumentListener()
182    {
183      public void insertUpdate(DocumentEvent ev)
184      {
185        checkSaveButton();
186      }
187
188      public void changedUpdate(DocumentEvent ev)
189      {
190        checkSaveButton();
191      }
192
193      public void removeUpdate(DocumentEvent ev)
194      {
195        checkSaveButton();
196      }
197    });
198
199    ActionListener listener = new ActionListener()
200    {
201      public void actionPerformed(ActionEvent ev)
202      {
203        checkSaveButton();
204      }
205    };
206    for (JCheckBox cb : types)
207    {
208      cb.addActionListener(listener);
209    }
210  }
211
212  /** {@inheritDoc} */
213  public LocalizableMessage getTitle()
214  {
215    return INFO_CTRL_PANEL_INDEX_PANEL_TITLE.get();
216  }
217
218  /** {@inheritDoc} */
219  public Component getPreferredFocusComponent()
220  {
221    return entryLimit;
222  }
223
224  /** {@inheritDoc} */
225  public void configurationChanged(ConfigurationChangeEvent ev)
226  {
227    final ServerDescriptor desc = ev.getNewDescriptor();
228    updateErrorPaneIfAuthRequired(desc,
229        isLocal() ?
230            INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_INDEX_EDITING.get() :
231      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
232    SwingUtilities.invokeLater(new Runnable()
233    {
234      public void run()
235      {
236        checkSaveButton();
237        deleteIndex.setEnabled(!authenticationRequired(desc));
238      }
239    });
240  }
241
242  /** {@inheritDoc} */
243  public void okClicked()
244  {
245  }
246
247  /**
248   * Method used to know if there are unsaved changes or not. It is used by the
249   * index selection listener when the user changes the selection.
250   *
251   * @return <CODE>true</CODE> if there are unsaved changes (and so the
252   *         selection of the index should be canceled) and <CODE>false</CODE>
253   *         otherwise.
254   */
255  public boolean mustCheckUnsavedChanges()
256  {
257    return index != null &&
258        saveChanges.isVisible() && saveChanges.isEnabled();
259  }
260
261  /**
262   * Tells whether the user chose to save the changes in the panel, to not save
263   * them or simply cancelled the selection in the tree.
264   *
265   * @return the value telling whether the user chose to save the changes in the
266   *         panel, to not save them or simply cancelled the selection change in
267   *         the tree.
268   */
269  public UnsavedChangesDialog.Result checkUnsavedChanges()
270  {
271    UnsavedChangesDialog.Result result;
272    UnsavedChangesDialog unsavedChangesDlg = new UnsavedChangesDialog(Utilities.getParentDialog(this), getInfo());
273    unsavedChangesDlg.setMessage(INFO_CTRL_PANEL_UNSAVED_CHANGES_SUMMARY.get(),
274                                 INFO_CTRL_PANEL_UNSAVED_INDEX_CHANGES_DETAILS.get(index.getName()));
275    Utilities.centerGoldenMean(unsavedChangesDlg, Utilities.getParentDialog(this));
276    unsavedChangesDlg.setVisible(true);
277    result = unsavedChangesDlg.getResult();
278    if (result == UnsavedChangesDialog.Result.SAVE)
279    {
280      saveIndex(false);
281      if (newModifyTask == null || // The user data is not valid
282          newModifyTask.getState() != Task.State.FINISHED_SUCCESSFULLY)
283      {
284        result = UnsavedChangesDialog.Result.CANCEL;
285      }
286    }
287
288    return result;
289  }
290
291  /** Checks the enabling state of the save button. */
292  private void checkSaveButton()
293  {
294    if (!ignoreCheckSave && index != null)
295    {
296      saveChanges.setEnabled(
297          !authenticationRequired(getInfo().getServerDescriptor()) &&
298          isModified());
299    }
300  }
301
302  private void deleteIndex()
303  {
304    List<LocalizableMessage> errors = new ArrayList<>();
305    ProgressDialog dlg = new ProgressDialog(
306        Utilities.createFrame(),
307        Utilities.getParentDialog(this),
308        INFO_CTRL_PANEL_DELETE_INDEX_TITLE.get(), getInfo());
309    ArrayList<AbstractIndexDescriptor> indexesToDelete = new ArrayList<>();
310    indexesToDelete.add(index);
311    DeleteIndexTask newTask = new DeleteIndexTask(getInfo(), dlg, indexesToDelete);
312    for (Task task : getInfo().getTasks())
313    {
314      task.canLaunch(newTask, errors);
315    }
316
317    if (errors.isEmpty())
318    {
319      String indexName = index.getName();
320      String backendName = index.getBackend().getBackendID();
321      if (displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
322                                    INFO_CTRL_PANEL_CONFIRMATION_INDEX_DELETE_DETAILS.get(indexName, backendName)))
323      {
324        launchOperation(newTask,
325            INFO_CTRL_PANEL_DELETING_INDEX_SUMMARY.get(),
326            INFO_CTRL_PANEL_DELETING_INDEX_COMPLETE.get(),
327            INFO_CTRL_PANEL_DELETING_INDEX_SUCCESSFUL.get(indexName, backendName),
328            ERR_CTRL_PANEL_DELETING_INDEX_ERROR_SUMMARY.get(),
329            ERR_CTRL_PANEL_DELETING_INDEX_ERROR_DETAILS.get(indexName), null, dlg);
330        dlg.setVisible(true);
331      }
332    }
333    else
334    {
335      displayErrorDialog(errors);
336    }
337  }
338
339  /**
340   * Saves the index modifications.
341   *
342   * @param modal
343   *          whether the progress dialog for the task must be modal or not.
344   */
345  private void saveIndex(boolean modal)
346  {
347    newModifyTask = null;
348    if (!isModified())
349    {
350      return;
351    }
352
353    List<LocalizableMessage> errors = getErrors();
354
355    if (errors.isEmpty())
356    {
357      ProgressDialog dlg = new ProgressDialog(
358          Utilities.getFrame(this),
359          Utilities.getFrame(this),
360          INFO_CTRL_PANEL_MODIFYING_INDEX_TITLE.get(), getInfo());
361      dlg.setModal(modal);
362      newModifyTask = new ModifyIndexTask(getInfo(), dlg);
363      for (Task task : getInfo().getTasks())
364      {
365        task.canLaunch(newModifyTask, errors);
366      }
367      if (errors.isEmpty())
368      {
369        String attributeName = index.getName();
370        String backendName = index.getBackend().getBackendID();
371        launchOperation(newModifyTask,
372            INFO_CTRL_PANEL_MODIFYING_INDEX_SUMMARY.get(attributeName),
373            INFO_CTRL_PANEL_MODIFYING_INDEX_COMPLETE.get(),
374            INFO_CTRL_PANEL_MODIFYING_INDEX_SUCCESSFUL.get(attributeName, backendName),
375            ERR_CTRL_PANEL_MODIFYING_INDEX_ERROR_SUMMARY.get(),
376            ERR_CTRL_PANEL_MODIFYING_INDEX_ERROR_DETAILS.get(attributeName), null, dlg);
377        saveChanges.setEnabled(false);
378        dlg.setVisible(true);
379      }
380    }
381
382    if (!errors.isEmpty())
383    {
384      displayErrorDialog(errors);
385    }
386  }
387
388  /**
389   * Updates the contents of the panel with the provided index.
390   *
391   * @param index
392   *          the index descriptor to be used to update the panel.
393   */
394  public void update(IndexDescriptor index)
395  {
396    ignoreCheckSave = true;
397    setPrimaryValid(lEntryLimit);
398    setPrimaryValid(lType);
399    name.setText(index.getName());
400    backendName.setText(index.getBackend().getBackendID());
401    titlePanel.setDetails(LocalizableMessage.raw(index.getName()));
402    entryLimit.setText(String.valueOf(index.getEntryLimit()));
403    approximate.setSelected(false);
404    equality.setSelected(false);
405    ordering.setSelected(false);
406    substring.setSelected(false);
407    presence.setSelected(false);
408    for (IndexTypeDescriptor type : index.getTypes())
409    {
410      switch(type)
411      {
412      case APPROXIMATE:
413        approximate.setSelected(true);
414        break;
415      case PRESENCE:
416        presence.setSelected(true);
417        break;
418      case EQUALITY:
419        equality.setSelected(true);
420        break;
421      case ORDERING:
422        ordering.setSelected(true);
423        break;
424      case SUBSTRING:
425        substring.setSelected(true);
426        break;
427      }
428    }
429
430    JComponent[] comps = {entryLimit, lType, typesPanel, lEntryLimit};
431
432    for (int i = 0; i < comps.length; i++)
433    {
434      comps[i].setVisible(!index.isDatabaseIndex());
435    }
436
437    AttributeType attr = index.getAttributeType();
438    repopulateTypesPanel(attr);
439
440    if (index.isDatabaseIndex())
441    {
442      entryLimit.setText("");
443    }
444    saveChanges.setVisible(!index.isDatabaseIndex());
445    deleteIndex.setVisible(!index.isDatabaseIndex());
446    if (index.isDatabaseIndex())
447    {
448      Utilities.setWarningLabel(warning, NON_CONFIGURABLE_INDEX);
449      warning.setVisible(true);
450    }
451    else if (getInfo() != null)
452    {
453      if (getInfo().mustReindex(index))
454      {
455        Utilities.setWarningLabel(warning, INDEX_MODIFIED);
456        warning.setVisible(true);
457        warning.setVerticalTextPosition(SwingConstants.TOP);
458      }
459      else
460      {
461        warning.setVisible(false);
462      }
463    }
464    this.index = index;
465
466    ignoreCheckSave = false;
467    checkSaveButton();
468
469    scrollListener.updateBorder();
470  }
471
472  /**
473   * Returns <CODE>true</CODE> if the index has been modified and
474   * <CODE>false</CODE> otherwise.
475   *
476   * @return <CODE>true</CODE> if the index has been modified and
477   *         <CODE>false</CODE> otherwise.
478   */
479  private boolean isModified()
480  {
481    return !getTypes().equals(index.getTypes()) ||
482    !String.valueOf(index.getEntryLimit()).equals(entryLimit.getText());
483  }
484
485  /**
486   * The task in charge of modifying the index.
487   */
488  protected class ModifyIndexTask extends Task
489  {
490    private Set<String> backendSet;
491    private String attributeName;
492    private String backendName;
493    private int entryLimitValue;
494    private IndexDescriptor indexToModify;
495    private SortedSet<IndexTypeDescriptor> indexTypes = new TreeSet<>();
496    private IndexDescriptor modifiedIndex;
497
498    /**
499     * The constructor of the task.
500     *
501     * @param info
502     *          the control panel info.
503     * @param dlg
504     *          the progress dialog that shows the progress of the task.
505     */
506    public ModifyIndexTask(ControlPanelInfo info, ProgressDialog dlg)
507    {
508      super(info, dlg);
509      backendName = index.getBackend().getBackendID();
510      backendSet = new HashSet<>();
511      backendSet.add(backendName);
512      attributeName = index.getName();
513      entryLimitValue = Integer.parseInt(entryLimit.getText());
514      indexTypes = getTypes();
515
516      indexToModify = index;
517    }
518
519    /** {@inheritDoc} */
520    public Type getType()
521    {
522      return Type.MODIFY_INDEX;
523    }
524
525    /** {@inheritDoc} */
526    public Set<String> getBackends()
527    {
528      return backendSet;
529    }
530
531    /** {@inheritDoc} */
532    public LocalizableMessage getTaskDescription()
533    {
534      return INFO_CTRL_PANEL_MODIFY_INDEX_TASK_DESCRIPTION.get(attributeName,
535          backendName);
536    }
537
538    /** {@inheritDoc} */
539    public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons)
540    {
541      boolean canLaunch = true;
542      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
543      {
544        // All the operations are incompatible if they apply to this
545        // backend for safety.  This is a short operation so the limitation
546        // has not a lot of impact.
547        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
548        backends.retainAll(getBackends());
549        if (!backends.isEmpty())
550        {
551          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
552          canLaunch = false;
553        }
554      }
555      return canLaunch;
556    }
557
558    /**
559     * Updates the configuration of the modified index.
560     *
561     * @throws OpenDsException
562     *           if there is an error updating the configuration.
563     */
564    private void updateConfiguration() throws OpenDsException
565    {
566      boolean configHandlerUpdated = false;
567      try
568      {
569        if (!isServerRunning())
570        {
571          configHandlerUpdated = true;
572          getInfo().stopPooling();
573          if (getInfo().mustDeregisterConfig())
574          {
575            DirectoryServer.deregisterBaseDN(DN.valueOf("cn=config"));
576          }
577          DirectoryServer.getInstance().initializeConfiguration(
578              org.opends.server.extensions.ConfigFileHandler.class.getName(), ConfigReader.configFile);
579          getInfo().setMustDeregisterConfig(true);
580        }
581        else
582        {
583          SwingUtilities.invokeLater(new Runnable()
584          {
585            public void run()
586            {
587              StringBuilder sb = new StringBuilder();
588              sb.append(getConfigCommandLineName());
589              List<String> args = getObfuscatedCommandLineArguments(getDSConfigCommandLineArguments());
590              args.removeAll(getConfigCommandLineArguments());
591
592              printEquivalentCommandLine(
593                      getConfigCommandLineName(), args, INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_MODIFY_INDEX.get());
594            }
595          });
596        }
597
598        SwingUtilities.invokeLater(new Runnable()
599        {
600          public void run()
601          {
602            getProgressDialog().appendProgressHtml(
603                Utilities.getProgressWithPoints(
604                    INFO_CTRL_PANEL_MODIFYING_INDEX_PROGRESS.get(attributeName),
605                    ColorAndFontConstants.progressFont));
606          }
607        });
608
609        if (isServerRunning())
610        {
611          modifyIndexOnline(getInfo().getDirContext());
612        }
613        else
614        {
615          modifyIndexOffline(backendName, attributeName, indexToModify, indexTypes, entryLimitValue);
616        }
617
618        SwingUtilities.invokeLater(new Runnable()
619        {
620          public void run()
621          {
622            getProgressDialog().appendProgressHtml(
623                Utilities.getProgressDone(ColorAndFontConstants.progressFont));
624          }
625        });
626      }
627      finally
628      {
629        if (configHandlerUpdated)
630        {
631          DirectoryServer.getInstance().initializeConfiguration(ConfigReader.configClassName, ConfigReader.configFile);
632          getInfo().startPooling();
633        }
634      }
635    }
636
637    /**
638     * Modifies index using the provided connection.
639     *
640     * @param ctx
641     *          the connection to be used to update the index configuration.
642     * @throws OpenDsException
643     *           if there is an error updating the server.
644     */
645    private void modifyIndexOnline(final InitialLdapContext ctx) throws OpenDsException
646    {
647      final ManagementContext mCtx = LDAPManagementContext.createFromContext(JNDIDirContextAdaptor.adapt(ctx));
648      final BackendCfgClient backend = mCtx.getRootConfiguration().getBackend(backendName);
649      if (backend instanceof LocalDBBackendCfgClient)
650      {
651        modifyLocalDBIndexOnline((LocalDBBackendCfgClient) backend);
652        return;
653      }
654      modifyBackendIndexOnline((PluggableBackendCfgClient) backend);
655    }
656
657    private void modifyBackendIndexOnline(final PluggableBackendCfgClient backend) throws OpenDsException
658    {
659      final BackendIndexCfgClient index = backend.getBackendIndex(attributeName);
660      if (!indexTypes.equals(indexToModify.getTypes()))
661      {
662        index.setIndexType(IndexTypeDescriptor.toBackendIndexTypes(indexTypes));
663      }
664
665      if (entryLimitValue != index.getIndexEntryLimit())
666      {
667        index.setIndexEntryLimit(entryLimitValue);
668      }
669      index.commit();
670    }
671
672    @RemoveOnceLocalDBBackendIsPluggable
673    private void modifyLocalDBIndexOnline(final LocalDBBackendCfgClient backend) throws OpenDsException
674    {
675      final LocalDBIndexCfgClient index = backend.getLocalDBIndex(attributeName);
676      if (!indexTypes.equals(indexToModify.getTypes()))
677      {
678        index.setIndexType(IndexTypeDescriptor.toLocalDBIndexTypes(indexTypes));
679      }
680
681      if (entryLimitValue != index.getIndexEntryLimit())
682      {
683        index.setIndexEntryLimit(entryLimitValue);
684      }
685      index.commit();
686    }
687
688    /** {@inheritDoc} */
689    protected String getCommandLinePath()
690    {
691      return null;
692    }
693
694    /** {@inheritDoc} */
695    protected ArrayList<String> getCommandLineArguments()
696    {
697      return new ArrayList<>();
698    }
699
700    /**
701     * Returns the full command-line path of the dsconfig command-line if we can
702     * provide an equivalent command-line (the server is running).
703     *
704     * @return the full command-line path of the dsconfig command-line if we can
705     *         provide an equivalent command-line (the server is running).
706     */
707    private String getConfigCommandLineName()
708    {
709      if (isServerRunning() && isModified())
710      {
711        return getCommandLinePath("dsconfig");
712      }
713      else
714      {
715        return null;
716      }
717    }
718
719    /** {@inheritDoc} */
720    public void runTask()
721    {
722      state = State.RUNNING;
723      lastException = null;
724
725      try
726      {
727        updateConfiguration();
728        modifiedIndex = new IndexDescriptor(attributeName,
729            indexToModify.getAttributeType(),
730            indexToModify.getBackend(),
731            indexTypes,
732            entryLimitValue);
733        getInfo().registerModifiedIndex(modifiedIndex);
734        state = State.FINISHED_SUCCESSFULLY;
735      }
736      catch (Throwable t)
737      {
738        lastException = t;
739        state = State.FINISHED_WITH_ERROR;
740      }
741    }
742
743    /** {@inheritDoc} */
744    public void postOperation()
745    {
746      if (lastException == null && state == State.FINISHED_SUCCESSFULLY)
747      {
748        rebuildIndexIfNecessary(modifiedIndex, getProgressDialog());
749      }
750    }
751
752    private List<String> getDSConfigCommandLineArguments()
753    {
754      List<String> args = new ArrayList<>();
755      args.add("set-local-db-index-prop");
756      args.add("--backend-name");
757      args.add(backendName);
758
759      args.add("--index-name");
760      args.add(attributeName);
761
762      if (!indexTypes.equals(indexToModify.getTypes()))
763      {
764        // To add
765        Set<IndexTypeDescriptor> toAdd = new TreeSet<>();
766        for (IndexTypeDescriptor newType : indexTypes)
767        {
768          if (!indexToModify.getTypes().contains(newType))
769          {
770            toAdd.add(newType);
771          }
772        }
773        // To delete
774        Set<IndexTypeDescriptor> toDelete = new TreeSet<>();
775        for (IndexTypeDescriptor oldType : indexToModify.getTypes())
776        {
777          if (!indexTypes.contains(oldType))
778          {
779            toDelete.add(oldType);
780          }
781        }
782        for (IndexTypeDescriptor newType : toDelete)
783        {
784          args.add("--remove");
785          args.add("index-type:" + newType);
786        }
787        for (IndexTypeDescriptor newType : toAdd)
788        {
789          args.add("--add");
790          args.add("index-type:" + newType.toLocalDBIndexType());
791        }
792      }
793      if (entryLimitValue != indexToModify.getEntryLimit())
794      {
795        args.add("--set");
796        args.add("index-entry-limit:"+entryLimitValue);
797      }
798      args.addAll(getConnectionCommandLineArguments());
799      args.add("--no-prompt");
800      return args;
801    }
802  }
803}