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.event.ItemEvent;
035import java.awt.event.ItemListener;
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.HashMap;
039import java.util.LinkedHashSet;
040import java.util.List;
041import java.util.Map;
042import java.util.Set;
043import java.util.SortedSet;
044import java.util.TreeSet;
045
046import javax.swing.ButtonGroup;
047import javax.swing.DefaultComboBoxModel;
048import javax.swing.JComboBox;
049import javax.swing.JLabel;
050import javax.swing.JRadioButton;
051import javax.swing.JScrollPane;
052import javax.swing.ListCellRenderer;
053import javax.swing.SwingUtilities;
054
055import org.forgerock.i18n.LocalizableMessage;
056import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
057import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
058import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement;
059import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
060import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
061import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
062import org.opends.guitools.controlpanel.datamodel.SortableListModel;
063import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor;
064import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
065import org.opends.guitools.controlpanel.event.IndexModifiedEvent;
066import org.opends.guitools.controlpanel.event.IndexModifiedListener;
067import org.opends.guitools.controlpanel.task.IndexTask;
068import org.opends.guitools.controlpanel.task.Task;
069import org.opends.guitools.controlpanel.ui.components.AddRemovePanel;
070import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer;
071import org.opends.guitools.controlpanel.ui.renderer.IndexCellRenderer;
072import org.opends.guitools.controlpanel.ui.renderer.IndexComboBoxCellRenderer;
073import org.opends.guitools.controlpanel.util.Utilities;
074import org.opends.guitools.controlpanel.util.ViewPositions;
075
076/**
077 * The panel that appears when the user wants to verify an index.
078 */
079public class VerifyIndexPanel extends StatusGenericPanel implements IndexModifiedListener
080{
081  private static final long serialVersionUID = 5252070109221657041L;
082  private JComboBox<?> baseDNs;
083  private JRadioButton verifyIndexContents;
084  private JRadioButton verifyKeyEntryIDs;
085  private AddRemovePanel<AbstractIndexDescriptor> addRemove;
086  private JComboBox<?> keyEntryIDs;
087  private Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes = new HashMap<>();
088
089  private JLabel lBaseDN;
090  private JLabel lAction;
091  private JLabel lIndex;
092  private JLabel lNoBaseDNsFound;
093
094  /** Constructor of the panel. */
095  public VerifyIndexPanel()
096  {
097    super();
098    createLayout();
099  }
100
101  /** {@inheritDoc} */
102  @Override
103  public void setInfo(ControlPanelInfo info)
104  {
105    super.setInfo(info);
106    ListCellRenderer indexCellRenderer = new IndexCellRenderer(addRemove.getAvailableList(), info);
107    addRemove.getAvailableList().setCellRenderer(indexCellRenderer);
108    addRemove.getSelectedList().setCellRenderer(indexCellRenderer);
109    info.addIndexModifiedListener(this);
110  }
111
112  /** {@inheritDoc} */
113  @Override
114  public void indexModified(IndexModifiedEvent ev)
115  {
116    refreshContents(getInfo().getServerDescriptor());
117  }
118
119  /** {@inheritDoc} */
120  @Override
121  public void backendIndexesModified(IndexModifiedEvent ev)
122  {
123    refreshContents(getInfo().getServerDescriptor());
124  }
125
126  /** Creates the layout of the panel (but the contents are not populated here). */
127  private void createLayout()
128  {
129    GridBagConstraints gbc = new GridBagConstraints();
130    gbc.anchor = GridBagConstraints.WEST;
131    gbc.weightx = 0.0;
132    gbc.gridx = 0;
133    gbc.gridy = 0;
134    gbc.gridwidth = 1;
135    gbc.fill = GridBagConstraints.NONE;
136    lBaseDN = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_LABEL.get());
137    add(lBaseDN, gbc);
138    gbc.insets.left = 10;
139    gbc.gridx = 1;
140    gbc.gridy = 0;
141    baseDNs = Utilities.createComboBox();
142    DefaultComboBoxModel model = new DefaultComboBoxModel();
143    baseDNs.setModel(model);
144    baseDNs.setRenderer(new CustomListCellRenderer(baseDNs));
145    ItemListener listener = new IgnoreItemListener(baseDNs);
146    baseDNs.addItemListener(listener);
147    baseDNs.addItemListener(new ItemListener()
148    {
149      @Override
150      public void itemStateChanged(ItemEvent ev)
151      {
152        comboBoxSelected(hmIndexes, (CategorizedComboBoxElement) baseDNs.getSelectedItem(), addRemove);
153        updateVerifyKeyEntriesComboBox();
154      }
155    });
156    listener.itemStateChanged(null);
157    gbc.gridwidth = 2;
158    add(baseDNs, gbc);
159    lNoBaseDNsFound = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_BASE_DNS_FOUND_LABEL.get());
160    add(lNoBaseDNsFound, gbc);
161    lNoBaseDNsFound.setVisible(false);
162
163    lAction = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_ACTION_LABEL.get());
164    gbc.insets.top = 10;
165    gbc.gridy++;
166    gbc.gridx = 0;
167    gbc.gridwidth = 1;
168    add(lAction, gbc);
169
170    verifyIndexContents = Utilities.createRadioButton(INFO_CTRL_PANEL_VERIFY_ENTRY_CONTEXT_ARE_INDEXES.get());
171    verifyIndexContents.setSelected(true);
172    gbc.insets.left = 10;
173    gbc.gridx = 1;
174    gbc.gridwidth = 2;
175    add(verifyIndexContents, gbc);
176
177    addRemove = new AddRemovePanel<>(AbstractIndexDescriptor.class);
178    addRemove.getAvailableLabel().setText(INFO_CTRL_PANEL_AVAILABLE_INDEXES_LABEL.get().toString());
179    addRemove.getSelectedLabel().setText(INFO_CTRL_PANEL_SELECTED_INDEXES_LABEL.get().toString());
180
181    gbc.gridy++;
182    gbc.weightx = 1.0;
183    gbc.weighty = 0.0;
184    gbc.gridwidth = 2;
185    gbc.insets.top = 5;
186    gbc.insets.left = 30;
187    gbc.fill = GridBagConstraints.BOTH;
188    add(addRemove, gbc);
189
190    gbc.gridy++;
191    JLabel explanation = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_REQUIRES_REBUILD_LEGEND.get());
192    gbc.insets.top = 3;
193    add(explanation, gbc);
194
195    verifyKeyEntryIDs = Utilities.createRadioButton(INFO_CTRL_PANEL_VERIFY_ALL_KEYS.get());
196    verifyKeyEntryIDs.setSelected(true);
197    gbc.insets.left = 10;
198    gbc.insets.top = 10;
199    gbc.gridx = 1;
200    gbc.gridwidth = 2;
201    gbc.gridy++;
202    gbc.weighty = 0.0;
203    gbc.fill = GridBagConstraints.NONE;
204    add(verifyKeyEntryIDs, gbc);
205
206    gbc.gridy++;
207    gbc.insets.left = 30;
208    gbc.insets.top = 5;
209    gbc.gridwidth = 1;
210    gbc.weightx = 0.0;
211    lIndex = Utilities.createDefaultLabel(INFO_CTRL_PANEL_INDEX_LABEL.get());
212    add(lIndex, gbc);
213
214    keyEntryIDs = Utilities.createComboBox();
215    model = new DefaultComboBoxModel();
216    keyEntryIDs.setModel(model);
217    keyEntryIDs.setRenderer(new IndexComboBoxCellRenderer(keyEntryIDs));
218    listener = new IgnoreItemListener(keyEntryIDs);
219    keyEntryIDs.addItemListener(listener);
220    listener.itemStateChanged(null);
221    gbc.gridx = 2;
222    gbc.insets.left = 5;
223    add(keyEntryIDs, gbc);
224
225    addBottomGlue(gbc);
226
227    ButtonGroup group = new ButtonGroup();
228    group.add(verifyIndexContents);
229    group.add(verifyKeyEntryIDs);
230    verifyIndexContents.setSelected(true);
231    listener = new ItemListener()
232    {
233      @Override
234      public void itemStateChanged(ItemEvent ev)
235      {
236        addRemove.setEnabled(verifyIndexContents.isSelected());
237        keyEntryIDs.setEnabled(!verifyIndexContents.isSelected());
238        lIndex.setEnabled(!verifyIndexContents.isSelected());
239      }
240    };
241    verifyIndexContents.addItemListener(listener);
242    listener.itemStateChanged(null);
243  }
244
245  /** {@inheritDoc} */
246  @Override
247  public LocalizableMessage getTitle()
248  {
249    return INFO_CTRL_PANEL_VERIFY_INDEXES_PANEL_TITLE.get();
250  }
251
252  /** {@inheritDoc} */
253  @Override
254  public Component getPreferredFocusComponent()
255  {
256    return baseDNs;
257  }
258
259  /** {@inheritDoc} */
260  @Override
261  public void configurationChanged(ConfigurationChangeEvent ev)
262  {
263    refreshContents(ev.getNewDescriptor());
264  }
265
266  private void refreshContents(final ServerDescriptor desc)
267  {
268    updateIndexMap(desc, hmIndexes);
269    updateBaseDNComboBoxModel((DefaultComboBoxModel) baseDNs.getModel(), desc);
270    SwingUtilities.invokeLater(new Runnable()
271    {
272      @Override
273      public void run()
274      {
275        ViewPositions pos;
276        JScrollPane scroll = Utilities.getContainingScroll(VerifyIndexPanel.this);
277        if (scroll != null)
278        {
279          pos = Utilities.getViewPositions(scroll);
280        }
281        else
282        {
283          pos = Utilities.getViewPositions(VerifyIndexPanel.this);
284        }
285        comboBoxSelected(hmIndexes, (CategorizedComboBoxElement) baseDNs.getSelectedItem(), addRemove);
286        updateVerifyKeyEntriesComboBox();
287        addRemove.getAvailableList().repaint();
288        addRemove.getSelectedList().repaint();
289        boolean comboVisible = baseDNs.getModel().getSize() > 0;
290        baseDNs.setVisible(comboVisible);
291        lNoBaseDNsFound.setVisible(!comboVisible);
292        Utilities.updateViewPositions(pos);
293
294        if (!desc.isLocal())
295        {
296          displayErrorMessage(INFO_CTRL_PANEL_SERVER_REMOTE_SUMMARY.get(),
297                              INFO_CTRL_PANEL_SERVER_MUST_BE_LOCAL_VERIFY_INDEX_SUMMARY.get());
298        }
299        else
300        {
301          displayMainPanel();
302        }
303        setEnabledOK(desc.isLocal());
304      }
305    });
306  }
307
308  /** {@inheritDoc} */
309  @Override
310  public void cancelClicked()
311  {
312    setPrimaryValid(lBaseDN);
313    setSecondaryValid(addRemove.getSelectedLabel());
314    setSecondaryValid(lIndex);
315    super.cancelClicked();
316  }
317
318  /** {@inheritDoc} */
319  @Override
320  public void okClicked()
321  {
322    setPrimaryValid(lBaseDN);
323    setSecondaryValid(addRemove.getSelectedLabel());
324    setSecondaryValid(lIndex);
325
326    final Set<LocalizableMessage> errors = new LinkedHashSet<>();
327    String baseDN = getSelectedBaseDN();
328
329    if (baseDN == null)
330    {
331      setPrimaryInvalid(lBaseDN);
332      if (baseDNs.getItemCount() == 0)
333      {
334        errors.add(ERR_CTRL_PANEL_NO_BASE_DNS_DEFINED_LABEL.get());
335      }
336      else
337      {
338        errors.add(ERR_CTRL_PANEL_MUST_SELECT_BASE_DN.get());
339      }
340    }
341
342    if (verifyIndexContents.isSelected())
343    {
344      SortableListModel<AbstractIndexDescriptor> model = addRemove.getSelectedListModel();
345      if (model.getSize() == 0)
346      {
347        setSecondaryInvalid(addRemove.getSelectedLabel());
348        errors.add(ERR_CTRL_PANEL_INDEX_TO_BE_VERIFIED_REQUIRED.get());
349      }
350    }
351    else
352    {
353      AbstractIndexDescriptor index = getSelectedIndex();
354
355      if (index == null)
356      {
357        setPrimaryInvalid(lIndex);
358        final boolean indexSelected = keyEntryIDs.getItemCount() == 0 && baseDN != null;
359        errors.add(indexSelected ? ERR_CTRL_PANEL_NO_INDEXES_FOR_BASEDN.get(baseDN)
360                                 : ERR_CTRL_PANEL_INDEX_MUST_BE_SELECTED.get());
361      }
362    }
363
364    if (errors.isEmpty())
365    {
366      ProgressDialog progressDialog =
367          new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this), getTitle(), getInfo());
368      VerifyIndexTask newTask = new VerifyIndexTask(getInfo(), progressDialog);
369      for (Task task : getInfo().getTasks())
370      {
371        task.canLaunch(newTask, errors);
372      }
373      if (errors.isEmpty())
374      {
375        launchOperation(newTask,
376            INFO_CTRL_PANEL_VERIFYING_INDEXES_SUMMARY.get(baseDN),
377            INFO_CTRL_PANEL_VERIFYING_INDEXES_SUCCESSFUL_SUMMARY.get(),
378            INFO_CTRL_PANEL_VERIFYING_INDEXES_SUCCESSFUL_DETAILS.get(),
379            ERR_CTRL_PANEL_VERIFYING_INDEXES_ERROR_SUMMARY.get(), null,
380            ERR_CTRL_PANEL_VERIFYING_INDEXES_ERROR_DETAILS, progressDialog);
381        progressDialog.setVisible(true);
382        Utilities.getParentDialog(this).setVisible(false);
383      }
384    }
385    if (!errors.isEmpty())
386    {
387      displayErrorDialog(errors);
388    }
389  }
390
391  /** {@inheritDoc} */
392  @Override
393  protected boolean displayBackend(BackendDescriptor backend)
394  {
395    return !backend.isConfigBackend() && (backend.getType() == BackendDescriptor.Type.LOCAL_DB
396                                          || backend.getType() == BackendDescriptor.Type.PLUGGABLE);
397  }
398
399  private String getSelectedBaseDN()
400  {
401    CategorizedComboBoxElement o = (CategorizedComboBoxElement) baseDNs.getSelectedItem();
402    return o != null ? (String) o.getValue() : null;
403  }
404
405  private AbstractIndexDescriptor getSelectedIndex()
406  {
407    CategorizedComboBoxElement o = (CategorizedComboBoxElement) keyEntryIDs.getSelectedItem();
408    return o != null ? (AbstractIndexDescriptor) o.getValue() : null;
409  }
410
411  private void updateVerifyKeyEntriesComboBox()
412  {
413    String dn = getSelectedBaseDN();
414    if (dn != null)
415    {
416      SortedSet<AbstractIndexDescriptor> indexes = hmIndexes.get(dn);
417      if (indexes != null)
418      {
419        List<CategorizedComboBoxElement> newElements = new ArrayList<>();
420        List<AbstractIndexDescriptor> databaseIndexes = new ArrayList<>();
421        List<AbstractIndexDescriptor> attributeIndexes = new ArrayList<>();
422        List<AbstractIndexDescriptor> vlvIndexes = new ArrayList<>();
423        for (AbstractIndexDescriptor index : indexes)
424        {
425          if (index instanceof IndexDescriptor)
426          {
427            IndexDescriptor standardIndex = (IndexDescriptor) index;
428            if (standardIndex.isDatabaseIndex())
429            {
430              databaseIndexes.add(standardIndex);
431            }
432            else
433            {
434              attributeIndexes.add(standardIndex);
435            }
436          }
437          else
438          {
439            vlvIndexes.add(index);
440          }
441        }
442        addNewElements(databaseIndexes, INFO_CTRL_PANEL_DATABASE_INDEXES.get().toString(), newElements);
443        addNewElements(attributeIndexes, INFO_CTRL_PANEL_ATTRIBUTE_INDEXES.get().toString(), newElements);
444        addNewElements(vlvIndexes, INFO_CTRL_PANEL_VLV_INDEXES.get().toString(), newElements);
445        updateComboBoxModel(newElements, (DefaultComboBoxModel) keyEntryIDs.getModel());
446      }
447    }
448  }
449
450  private void addNewElements(final List<AbstractIndexDescriptor> indexes, final String label,
451      final List<CategorizedComboBoxElement> elements)
452  {
453    if (!indexes.isEmpty())
454    {
455      elements.add(new CategorizedComboBoxElement(label, CategorizedComboBoxElement.Type.CATEGORY));
456      for (AbstractIndexDescriptor index : indexes)
457      {
458        elements.add(new CategorizedComboBoxElement(index, CategorizedComboBoxElement.Type.REGULAR));
459      }
460    }
461  }
462
463  /** The task in charge of verifying the index. */
464  protected class VerifyIndexTask extends IndexTask
465  {
466    private String baseDN;
467
468    /**
469     * The constructor of the task.
470     *
471     * @param info
472     *          the control panel info.
473     * @param dlg
474     *          the progress dialog that shows the progress of the task.
475     */
476    public VerifyIndexTask(ControlPanelInfo info, ProgressDialog dlg)
477    {
478      super(info, dlg, getSelectedBaseDN());
479      this.baseDN = getSelectedBaseDN();
480    }
481
482    /** {@inheritDoc} */
483    @Override
484    public Type getType()
485    {
486      return Type.VERIFY_INDEXES;
487    }
488
489    /** {@inheritDoc} */
490    @Override
491    public LocalizableMessage getTaskDescription()
492    {
493      return INFO_CTRL_PANEL_VERIFY_INDEX_TASK_DESCRIPTION.get(baseDN);
494    }
495
496    /** {@inheritDoc} */
497    @Override
498    public boolean canLaunch(Task taskToBeLaunched, Collection<LocalizableMessage> incompatibilityReasons)
499    {
500      boolean canLaunch = true;
501      if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
502      {
503        // All the operations are incompatible if they apply to this backend.
504        Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
505        backends.retainAll(getBackends());
506        Task.Type type = taskToBeLaunched.getType();
507        if (type != Task.Type.BACKUP
508            && type != Task.Type.EXPORT_LDIF
509            && type != Task.Type.ENABLE_WINDOWS_SERVICE
510            && type != Task.Type.DISABLE_WINDOWS_SERVICE
511            && !backends.isEmpty())
512        {
513          incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
514          canLaunch = false;
515        }
516      }
517      return canLaunch;
518    }
519
520    /** {@inheritDoc} */
521    @Override
522    public void runTask()
523    {
524      state = State.RUNNING;
525      lastException = null;
526      try
527      {
528        List<String> arguments = getCommandLineArguments();
529        String[] args = arguments.toArray(new String[arguments.size()]);
530
531        returnCode = executeCommandLine(getCommandLinePath(), args);
532        state = returnCode == 0 ? State.FINISHED_SUCCESSFULLY : State.FINISHED_WITH_ERROR;
533      }
534      catch (Throwable t)
535      {
536        lastException = t;
537        state = State.FINISHED_WITH_ERROR;
538      }
539    }
540
541    /** {@inheritDoc} */
542    @Override
543    protected List<String> getCommandLineArguments()
544    {
545      List<String> args = new ArrayList<>();
546
547      args.add("--baseDN");
548      args.add(getSelectedBaseDN());
549
550      if (verifyIndexContents.isSelected())
551      {
552        SortableListModel<AbstractIndexDescriptor> model = addRemove.getSelectedListModel();
553        for (AbstractIndexDescriptor index : model.getData())
554        {
555          args.add("--index");
556          args.add(getName(index));
557        }
558      }
559      else
560      {
561        args.add("--index");
562        getName(getSelectedIndex());
563        args.add("--clean");
564      }
565
566      args.add("--countErrors");
567
568      return args;
569    }
570
571    private String getName(AbstractIndexDescriptor index)
572    {
573      if (index instanceof VLVIndexDescriptor)
574      {
575        return Utilities.getVLVNameInCommandLine((VLVIndexDescriptor) index);
576      }
577      return index.getName();
578    }
579
580    /** {@inheritDoc} */
581    @Override
582    protected String getCommandLinePath()
583    {
584      return getCommandLinePath("verify-index");
585    }
586  }
587}