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