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