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