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