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