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 */
017package org.opends.guitools.controlpanel.ui;
018
019import static org.opends.messages.AdminToolMessages.*;
020
021import java.awt.Component;
022import java.awt.GridBagConstraints;
023import java.awt.event.ItemEvent;
024import java.awt.event.ItemListener;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.LinkedHashSet;
029import java.util.Map;
030import java.util.Set;
031import java.util.SortedSet;
032
033import javax.swing.DefaultComboBoxModel;
034import javax.swing.JComboBox;
035import javax.swing.JLabel;
036import javax.swing.JScrollPane;
037import javax.swing.ListCellRenderer;
038import javax.swing.SwingUtilities;
039
040import org.forgerock.i18n.LocalizableMessage;
041import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
042import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
043import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement;
044import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
045import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
046import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
047import org.opends.guitools.controlpanel.datamodel.SortableListModel;
048import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
049import org.opends.guitools.controlpanel.event.IndexModifiedEvent;
050import org.opends.guitools.controlpanel.event.IndexModifiedListener;
051import org.opends.guitools.controlpanel.task.RebuildIndexTask;
052import org.opends.guitools.controlpanel.task.Task;
053import org.opends.guitools.controlpanel.ui.components.AddRemovePanel;
054import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer;
055import org.opends.guitools.controlpanel.ui.renderer.IndexCellRenderer;
056import org.opends.guitools.controlpanel.util.Utilities;
057import org.opends.guitools.controlpanel.util.ViewPositions;
058
059/** The panel that appears when the user wants to rebuild indexes. */
060public class RebuildIndexPanel extends StatusGenericPanel implements IndexModifiedListener
061{
062  private static final long serialVersionUID = -4805445967165643375L;
063  private JComboBox baseDNs;
064  private AddRemovePanel<AbstractIndexDescriptor> addRemove;
065
066  private JLabel lBaseDN;
067  private JLabel lIndexes;
068  private JLabel lNoBaseDNsFound;
069
070  private final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes = new HashMap<>();
071
072  /** Constructor of the panel. */
073  public RebuildIndexPanel()
074  {
075    createLayout();
076  }
077
078  @Override
079  public void indexModified(final IndexModifiedEvent ev)
080  {
081    refreshContents(getInfo().getServerDescriptor());
082  }
083
084  @Override
085  public void backendIndexesModified(final IndexModifiedEvent ev)
086  {
087    refreshContents(getInfo().getServerDescriptor());
088  }
089
090  /** Creates the layout of the panel (but the contents are not populated here). */
091  private void createLayout()
092  {
093    GridBagConstraints gbc = new GridBagConstraints();
094    gbc.anchor = GridBagConstraints.WEST;
095    gbc.weightx = 0.0;
096    gbc.gridx = 0;
097    gbc.gridy = 0;
098    gbc.gridwidth = 3;
099    addErrorPane(gbc);
100    gbc.gridwidth = 1;
101    gbc.fill = GridBagConstraints.NONE;
102    lBaseDN = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_BASE_DN_LABEL.get());
103    add(lBaseDN, gbc);
104    gbc.insets.left = 10;
105    gbc.gridx = 1;
106    gbc.gridy = 0;
107    baseDNs = Utilities.createComboBox();
108    DefaultComboBoxModel model = new DefaultComboBoxModel();
109    baseDNs.setModel(model);
110    baseDNs.setRenderer(new CustomListCellRenderer(baseDNs));
111    ItemListener listener = new IgnoreItemListener(baseDNs);
112    baseDNs.addItemListener(listener);
113    baseDNs.addItemListener(new ItemListener()
114    {
115      @Override
116      public void itemStateChanged(final ItemEvent ev)
117      {
118        comboBoxSelected(hmIndexes, (CategorizedComboBoxElement) baseDNs.getSelectedItem(), addRemove);
119      }
120    });
121    listener.itemStateChanged(null);
122    add(baseDNs, gbc);
123    lNoBaseDNsFound = Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_BASE_DNS_FOUND_LABEL.get());
124    add(lNoBaseDNsFound, gbc);
125    lNoBaseDNsFound.setVisible(false);
126
127    lIndexes = Utilities.createPrimaryLabel(INFO_CTRL_PANEL_INDEXES_LABEL.get());
128    gbc.insets.top = 10;
129    gbc.insets.left = 0;
130    gbc.gridy++;
131    gbc.gridx = 0;
132    gbc.gridwidth = 1;
133    gbc.anchor = GridBagConstraints.NORTHWEST;
134    add(lIndexes, gbc);
135
136    addRemove = new AddRemovePanel<>(AbstractIndexDescriptor.class);
137    addRemove.getAvailableLabel().setText(INFO_CTRL_PANEL_AVAILABLE_INDEXES_LABEL.get().toString());
138    addRemove.getSelectedLabel().setText(INFO_CTRL_PANEL_SELECTED_INDEXES_LABEL.get().toString());
139
140    gbc.gridx = 1;
141    gbc.weightx = 1.0;
142    gbc.weighty = 0.0;
143    gbc.gridwidth = 1;
144    gbc.insets.top = 10;
145    gbc.insets.left = 10;
146    gbc.fill = GridBagConstraints.BOTH;
147    add(addRemove, gbc);
148
149    gbc.gridy++;
150    gbc.insets.top = 3;
151    JLabel explanation = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_REQUIRES_REBUILD_LEGEND.get());
152    add(explanation, gbc);
153
154    addBottomGlue(gbc);
155  }
156
157  @Override
158  public void setInfo(final ControlPanelInfo info)
159  {
160    super.setInfo(info);
161    ListCellRenderer indexCellRenderer = new IndexCellRenderer(addRemove.getAvailableList(), info);
162    addRemove.getAvailableList().setCellRenderer(indexCellRenderer);
163    addRemove.getSelectedList().setCellRenderer(indexCellRenderer);
164    info.addIndexModifiedListener(this);
165  }
166
167  @Override
168  public LocalizableMessage getTitle()
169  {
170    return INFO_CTRL_PANEL_REBUILD_INDEXES_TITLE.get();
171  }
172
173  @Override
174  public Component getPreferredFocusComponent()
175  {
176    return baseDNs;
177  }
178
179  @Override
180  public void configurationChanged(final ConfigurationChangeEvent ev)
181  {
182    refreshContents(ev.getNewDescriptor());
183  }
184
185  /**
186   * Refresh the contents of the panel with the provided server descriptor.
187   *
188   * @param desc
189   *          the server descriptor.
190   */
191  private void refreshContents(final ServerDescriptor desc)
192  {
193    super.updateIndexMap(desc, hmIndexes);
194    filterIndexes(hmIndexes);
195
196    updateBaseDNComboBoxModel((DefaultComboBoxModel) baseDNs.getModel(), desc);
197
198    if (!allDisabled(desc.getBackends()))
199    {
200      updateErrorPaneAndOKButtonIfAuthRequired(desc,
201              isLocal() ? INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_DISABLE_BACKEND.get()
202                        : INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
203    }
204    SwingUtilities.invokeLater(new Runnable()
205    {
206      @Override
207      public void run()
208      {
209        ViewPositions pos;
210        JScrollPane scroll = Utilities.getContainingScroll(RebuildIndexPanel.this);
211        if (scroll != null)
212        {
213          pos = Utilities.getViewPositions(scroll);
214        }
215        else
216        {
217          pos = Utilities.getViewPositions(RebuildIndexPanel.this);
218        }
219
220        boolean comboVisible = baseDNs.getModel().getSize() > 0;
221        baseDNs.setVisible(comboVisible);
222        lNoBaseDNsFound.setVisible(!comboVisible);
223        addRemove.getAvailableList().repaint();
224        addRemove.getSelectedList().repaint();
225
226        Utilities.updateViewPositions(pos);
227        if (!desc.isLocal())
228        {
229          displayErrorMessage(INFO_CTRL_PANEL_SERVER_REMOTE_SUMMARY.get(),
230              INFO_CTRL_PANEL_SERVER_MUST_BE_LOCAL_REBUILD_INDEX_SUMMARY.get());
231          setEnabledOK(false);
232        }
233        else
234        {
235          displayMainPanel();
236          setEnabledOK(true);
237        }
238      }
239    });
240  }
241
242  private boolean allDisabled(Set<BackendDescriptor> backends)
243  {
244    for (BackendDescriptor backend : backends)
245    {
246      if (displayBackend(backend) && backend.isEnabled())
247      {
248        return false;
249      }
250    }
251    return true;
252  }
253
254  @Override
255  public void cancelClicked()
256  {
257    setPrimaryValid(lBaseDN);
258    setSecondaryValid(addRemove.getSelectedLabel());
259    super.cancelClicked();
260  }
261
262  @Override
263  public void okClicked()
264  {
265    setPrimaryValid(lBaseDN);
266    setSecondaryValid(addRemove.getSelectedLabel());
267
268    final Set<LocalizableMessage> errors = new LinkedHashSet<>();
269    String baseDN = getSelectedBaseDN();
270
271    if (baseDN == null)
272    {
273      setPrimaryInvalid(lBaseDN);
274      if (baseDNs.getItemCount() == 0)
275      {
276        errors.add(ERR_CTRL_PANEL_NO_BASE_DNS_DEFINED_LABEL.get());
277      }
278      else
279      {
280        errors.add(ERR_CTRL_PANEL_MUST_SELECT_BASE_DN.get());
281      }
282    }
283
284    SortableListModel<AbstractIndexDescriptor> model = addRemove.getSelectedListModel();
285    if (model.getSize() == 0)
286    {
287      setSecondaryInvalid(addRemove.getSelectedLabel());
288      errors.add(ERR_CTRL_PANEL_MUST_SELECT_INDEX_TO_REBUILD.get());
289    }
290
291    if (errors.isEmpty())
292    {
293      ProgressDialog progressDialog =
294          new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this), getTitle(), getInfo());
295      Set<String> baseDNs = new HashSet<>();
296      baseDNs.add(getSelectedBaseDN());
297      RebuildIndexTask newTask =
298          new RebuildIndexTask(getInfo(), progressDialog, baseDNs, addRemove.getSelectedListModel().getData());
299      for (Task task : getInfo().getTasks())
300      {
301        task.canLaunch(newTask, errors);
302      }
303      boolean confirmed = true;
304
305      if (errors.isEmpty() && isServerRunning())
306      {
307        String backendName = newTask.getBackends().iterator().next();
308        confirmed =
309            displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
310                INFO_CTRL_PANEL_CONFIRM_REBUILD_INDEX_DETAILS.get(backendName));
311      }
312      if (errors.isEmpty() && confirmed)
313      {
314        launchOperation(newTask, INFO_CTRL_PANEL_REBUILDING_INDEXES_SUMMARY.get(baseDN),
315            INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_SUMMARY.get(),
316            INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_DETAILS.get(),
317            ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_SUMMARY.get(), null,
318            ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_DETAILS, progressDialog);
319        progressDialog.setVisible(true);
320        Utilities.getParentDialog(this).setVisible(false);
321      }
322    }
323    if (!errors.isEmpty())
324    {
325      displayErrorDialog(errors);
326    }
327  }
328
329  @Override
330  protected boolean displayBackend(final BackendDescriptor backend)
331  {
332    return !backend.isConfigBackend() && backend.getType() == BackendDescriptor.Type.PLUGGABLE;
333  }
334
335  private String getSelectedBaseDN()
336  {
337    CategorizedComboBoxElement o = (CategorizedComboBoxElement) baseDNs.getSelectedItem();
338    return o != null ? (String) o.getValue() : null;
339  }
340
341  private void filterIndexes(final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes)
342  {
343    // Remove the indexes that are not to be added.
344    for (SortedSet<AbstractIndexDescriptor> indexes : hmIndexes.values())
345    {
346      for (Iterator<AbstractIndexDescriptor> it = indexes.iterator(); it.hasNext();)
347      {
348        if (!mustBeDisplayed(it.next()))
349        {
350          it.remove();
351        }
352      }
353    }
354  }
355
356  private boolean mustBeDisplayed(final AbstractIndexDescriptor index)
357  {
358    if (index instanceof IndexDescriptor)
359    {
360      for (String name : RebuildIndexTask.INDEXES_NOT_TO_SPECIFY)
361      {
362        if (name.equalsIgnoreCase(index.getName()))
363        {
364          return false;
365        }
366      }
367    }
368    return true;
369  }
370}