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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.guitools.controlpanel.ui.ControlCenterMainPane.*;
030import static org.opends.messages.AdminToolMessages.*;
031
032import java.awt.CardLayout;
033import java.awt.Color;
034import java.awt.Component;
035import java.awt.Container;
036import java.awt.Dimension;
037import java.awt.Font;
038import java.awt.GridBagConstraints;
039import java.awt.GridBagLayout;
040import java.awt.Insets;
041import java.awt.Window;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.awt.event.ItemEvent;
045import java.awt.event.ItemListener;
046import java.text.DateFormat;
047import java.text.SimpleDateFormat;
048import java.util.ArrayList;
049import java.util.Collection;
050import java.util.Comparator;
051import java.util.Date;
052import java.util.HashMap;
053import java.util.HashSet;
054import java.util.LinkedHashSet;
055import java.util.List;
056import java.util.Map;
057import java.util.Set;
058import java.util.SortedSet;
059import java.util.TreeSet;
060
061import javax.naming.NamingEnumeration;
062import javax.naming.directory.SearchControls;
063import javax.naming.directory.SearchResult;
064import javax.swing.Box;
065import javax.swing.ComboBoxModel;
066import javax.swing.DefaultComboBoxModel;
067import javax.swing.JComboBox;
068import javax.swing.JComponent;
069import javax.swing.JEditorPane;
070import javax.swing.JLabel;
071import javax.swing.JMenuBar;
072import javax.swing.JPanel;
073import javax.swing.SwingUtilities;
074import javax.swing.border.Border;
075
076import org.forgerock.i18n.LocalizableMessage;
077import org.forgerock.i18n.LocalizableMessageBuilder;
078import org.forgerock.i18n.LocalizableMessageDescriptor;
079import org.forgerock.i18n.slf4j.LocalizedLogger;
080import org.forgerock.opendj.ldap.schema.ObjectClassType;
081import org.opends.admin.ads.util.ConnectionUtils;
082import org.opends.guitools.controlpanel.browser.BrowserController;
083import org.opends.guitools.controlpanel.browser.IconPool;
084import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
085import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
086import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
087import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement;
088import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
089import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
090import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes;
091import org.opends.guitools.controlpanel.datamodel.ScheduleType;
092import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
093import org.opends.guitools.controlpanel.datamodel.SortableListModel;
094import org.opends.guitools.controlpanel.event.ConfigChangeListener;
095import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
096import org.opends.guitools.controlpanel.event.ConfigurationElementCreatedListener;
097import org.opends.guitools.controlpanel.task.RebuildIndexTask;
098import org.opends.guitools.controlpanel.task.RestartServerTask;
099import org.opends.guitools.controlpanel.task.StartServerTask;
100import org.opends.guitools.controlpanel.task.StopServerTask;
101import org.opends.guitools.controlpanel.task.Task;
102import org.opends.guitools.controlpanel.ui.components.AddRemovePanel;
103import org.opends.guitools.controlpanel.util.BackgroundTask;
104import org.opends.guitools.controlpanel.util.LowerCaseComparator;
105import org.opends.guitools.controlpanel.util.Utilities;
106import org.opends.quicksetup.ui.CustomHTMLEditorKit;
107import org.opends.server.schema.SchemaConstants;
108import org.opends.server.types.ObjectClass;
109import org.opends.server.types.OpenDsException;
110import org.opends.server.util.ServerConstants;
111import org.opends.server.util.StaticUtils;
112
113/**
114 * An abstract class that contains a number of methods that are shared by all
115 * the inheriting classes. In general a StatusGenericPanel is contained in a
116 * GenericDialog and specifies the kind of buttons that this dialog has. The
117 * StatusGenericPanel is also notified when the dialog is displayed (through the
118 * toBeDisplayed method)
119 */
120public abstract class StatusGenericPanel extends JPanel implements ConfigChangeListener
121{
122  private static final long serialVersionUID = -9123358652232556732L;
123
124  /**
125   * The string to be used as combo separator.
126   */
127  public static final String COMBO_SEPARATOR = "----------";
128
129  /**
130   * The not applicable message.
131   */
132  protected static final LocalizableMessage NOT_APPLICABLE = INFO_NOT_APPLICABLE_LABEL.get();
133
134  private static final LocalizableMessage AUTHENTICATE = INFO_AUTHENTICATE_BUTTON_LABEL.get();
135  private static final LocalizableMessage START = INFO_START_BUTTON_LABEL.get();
136
137  private ControlPanelInfo info;
138
139  private final boolean enableClose = true;
140  private boolean enableCancel = true;
141  private boolean enableOK = true;
142
143  private boolean disposeOnClose;
144
145  private final JPanel cardPanel;
146  private final JPanel mainPanel;
147  private final JEditorPane message;
148
149  private final CardLayout cardLayout;
150
151  private static final String MAIN_PANEL = "mainPanel";
152  private static final String MESSAGE_PANEL = "messagePanel";
153
154  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
155
156  /** The error pane. */
157  protected JEditorPane errorPane;
158
159  /** The last displayed message in the error pane. */
160  private String lastDisplayedError;
161
162  private final List<ConfigurationElementCreatedListener> confListeners = new ArrayList<>();
163
164  private boolean sizeSet;
165  private boolean focusSet;
166
167  private static final DateFormat taskDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
168
169  /**
170   * Returns the title that will be used as title of the dialog.
171   *
172   * @return the title that will be used as title of the dialog.
173   */
174  public abstract LocalizableMessage getTitle();
175
176  /**
177   * Returns the buttons that the dialog where this panel is contained should
178   * display.
179   *
180   * @return the buttons that the dialog where this panel is contained should
181   *         display.
182   */
183  public GenericDialog.ButtonType getButtonType()
184  {
185    return GenericDialog.ButtonType.OK_CANCEL;
186  }
187
188  /**
189   * Returns the component that should get the focus when the dialog that
190   * contains this panel is displayed.
191   *
192   * @return the component that should get the focus.
193   */
194  public abstract Component getPreferredFocusComponent();
195
196  /**
197   * Returns <CODE>true</CODE> if this panel requires some bordering (in general
198   * an EmptyBorder with some insets) and <CODE>false</CODE> otherwise.
199   *
200   * @return <CODE>true</CODE> if this panel requires some bordering (in general
201   *         an EmptyBorder with some insets) and <CODE>false</CODE> otherwise.
202   */
203  public boolean requiresBorder()
204  {
205    return true;
206  }
207
208  /**
209   * Returns the menu bar that the panel might have. Returns <CODE>null</CODE>
210   * if the panel has no menu bar associated.
211   *
212   * @return the menu bar that the panel might have.
213   */
214  public JMenuBar getMenuBar()
215  {
216    return null;
217  }
218
219  /**
220   * This method is called to indicate that the configuration changes should be
221   * called in the background. In the case of panels which require some time to
222   * be updated with the new configuration this method returns <CODE>true</CODE>
223   * and the operation will be performed in the background while a message of
224   * type 'Loading...' is displayed on the panel.
225   *
226   * @return <CODE>true</CODE> if changes should be loaded in the background and
227   *         <CODE>false</CODE> otherwise.
228   */
229  public boolean callConfigurationChangedInBackground()
230  {
231    return false;
232  }
233
234  /**
235   * The panel is notified that the dialog is going to be visible or invisible.
236   *
237   * @param visible
238   *          whether is going to be visible or not.
239   */
240  public void toBeDisplayed(final boolean visible)
241  {
242  }
243
244  /**
245   * Tells whether this panel should be contained in a scroll pane or not.
246   *
247   * @return <CODE>true</CODE> if this panel should be contained in a scroll
248   *         pane and <CODE>false</CODE> otherwise.
249   */
250  public boolean requiresScroll()
251  {
252    return true;
253  }
254
255  /**
256   * Constructor.
257   */
258  protected StatusGenericPanel()
259  {
260    super(new GridBagLayout());
261    setBackground(ColorAndFontConstants.background);
262
263    cardLayout = new CardLayout();
264    cardPanel = new JPanel(cardLayout);
265    cardPanel.setOpaque(false);
266
267    mainPanel = new JPanel(new GridBagLayout());
268    mainPanel.setOpaque(false);
269
270    message = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont);
271
272    GridBagConstraints gbc = new GridBagConstraints();
273    gbc.gridx = 0;
274    gbc.gridy = 0;
275    gbc.fill = GridBagConstraints.BOTH;
276    gbc.weightx = 1.0;
277    gbc.weighty = 1.0;
278    super.add(cardPanel, gbc);
279
280    cardPanel.add(mainPanel, MAIN_PANEL);
281
282    JPanel messagePanel = new JPanel(new GridBagLayout());
283    messagePanel.setOpaque(false);
284    gbc.fill = GridBagConstraints.NONE;
285    gbc.anchor = GridBagConstraints.CENTER;
286    messagePanel.add(message, gbc);
287    cardPanel.add(messagePanel, MESSAGE_PANEL);
288
289    cardLayout.show(cardPanel, MAIN_PANEL);
290  }
291
292  /**
293   * The components are not added directly to the panel but to the main panel.
294   * This is done to be able to display a message that takes the whole panel (of
295   * type 'Loading...') when we are doing long operations.
296   *
297   * @param comp
298   *          the Component to be added.
299   * @param constraints
300   *          the constraints.
301   */
302  @Override
303  public void add(final Component comp, final Object constraints)
304  {
305    mainPanel.add(comp, constraints);
306  }
307
308  /**
309   * Adds a bottom glue to the main panel with the provided constraints.
310   *
311   * @param gbc
312   *          the constraints.
313   */
314  protected void addBottomGlue(final GridBagConstraints gbc)
315  {
316    GridBagConstraints gbc2 = (GridBagConstraints) gbc.clone();
317    gbc2.insets = new Insets(0, 0, 0, 0);
318    gbc2.gridy++;
319    gbc2.gridwidth = GridBagConstraints.REMAINDER;
320    gbc2.weighty = 1.0;
321    gbc2.fill = GridBagConstraints.VERTICAL;
322    add(Box.createVerticalGlue(), gbc2);
323    gbc.gridy++;
324  }
325
326  /**
327   * Returns a label with text 'Required Field' and an icon (used as legend in
328   * some panels).
329   *
330   * @return a label with text 'Required Field' and an icon (used as legend in
331   *         some panels).
332   */
333  protected JLabel createRequiredLabel()
334  {
335    JLabel requiredLabel = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_INDICATES_REQUIRED_FIELD_LABEL.get());
336    requiredLabel.setIcon(Utilities.createImageIcon(IconPool.IMAGE_PATH + "/required.gif"));
337
338    return requiredLabel;
339  }
340
341  /**
342   * Creates and adds an error pane. Is up to the caller to set the proper
343   * gridheight, gridwidth, gridx and gridy on the provided GridBagConstraints.
344   *
345   * @param baseGbc
346   *          the GridBagConstraints to be used.
347   */
348  protected void addErrorPane(final GridBagConstraints baseGbc)
349  {
350    addErrorPane(this, baseGbc);
351  }
352
353  /**
354   * Adds an error pane to the provided container. Is up to the caller to set
355   * the proper gridheight, gridwidth, gridx and gridy on the provided
356   * GridBagConstraints.
357   *
358   * @param baseGbc
359   *          the GridBagConstraints to be used.
360   * @param p
361   *          the container.
362   */
363  protected void addErrorPane(final Container p, final GridBagConstraints baseGbc)
364  {
365    GridBagConstraints gbc = new GridBagConstraints();
366    gbc.gridx = baseGbc.gridx;
367    gbc.gridy = baseGbc.gridy;
368    gbc.gridwidth = baseGbc.gridwidth;
369    gbc.gridheight = baseGbc.gridheight;
370    gbc.weightx = 1.0;
371    gbc.fill = GridBagConstraints.BOTH;
372    if (requiresBorder())
373    {
374      gbc.insets = new Insets(0, 0, 10, 0);
375    }
376    else
377    {
378      gbc.insets = new Insets(20, 20, 0, 20);
379    }
380    createErrorPane();
381    p.add(errorPane, gbc);
382  }
383
384  /**
385   * Creates the error pane.
386   */
387  protected void createErrorPane()
388  {
389    errorPane = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont);
390    errorPane.setOpaque(false);
391    errorPane.setEditable(false);
392    errorPane.setVisible(false);
393    CustomHTMLEditorKit htmlEditor = new CustomHTMLEditorKit();
394    htmlEditor.addActionListener(new ActionListener()
395    {
396      @Override
397      public void actionPerformed(final ActionEvent ev)
398      {
399        if (AUTHENTICATE.toString().equals(ev.getActionCommand()))
400        {
401          authenticate();
402        }
403        else if (START.toString().equals(ev.getActionCommand()))
404        {
405          startServer();
406        }
407      }
408    });
409    errorPane.setEditorKit(htmlEditor);
410  }
411
412  /**
413   * Commodity method used to add lines, where each line contains a label, a
414   * component and an inline help label.
415   *
416   * @param labels
417   *          the labels.
418   * @param comps
419   *          the components.
420   * @param inlineHelp
421   *          the inline help labels.
422   * @param panel
423   *          the panel where we will add the lines.
424   * @param gbc
425   *          the grid bag constraints.
426   */
427  protected void add(final JLabel[] labels, final Component[] comps, final JLabel[] inlineHelp, final Container panel,
428      final GridBagConstraints gbc)
429  {
430    int i = 0;
431    for (Component comp : comps)
432    {
433      gbc.insets.left = 0;
434      gbc.weightx = 0.0;
435      gbc.gridx = 0;
436      if (labels[i] != null)
437      {
438        panel.add(labels[i], gbc);
439      }
440      gbc.insets.left = 10;
441      gbc.weightx = 1.0;
442      gbc.gridx = 1;
443      panel.add(comp, gbc);
444      if (inlineHelp[i] != null)
445      {
446        gbc.insets.top = 3;
447        gbc.gridy++;
448        panel.add(inlineHelp[i], gbc);
449      }
450      gbc.insets.top = 10;
451      gbc.gridy++;
452      i++;
453    }
454  }
455
456  /**
457   * Enables the OK button in the parent dialog.
458   *
459   * @param enable
460   *          whether to enable or disable the button.
461   */
462  protected void setEnabledOK(final boolean enable)
463  {
464    Window w = Utilities.getParentDialog(this);
465    if (w instanceof GenericDialog)
466    {
467      ((GenericDialog) w).setEnabledOK(enable);
468    }
469    else if (w instanceof GenericFrame)
470    {
471      ((GenericFrame) w).setEnabledOK(enable);
472    }
473    enableOK = enable;
474  }
475
476  /**
477   * Enables the Cancel button in the parent dialog.
478   *
479   * @param enable
480   *          whether to enable or disable the button.
481   */
482  protected void setEnabledCancel(final boolean enable)
483  {
484    Window w = Utilities.getParentDialog(this);
485    if (w instanceof GenericDialog)
486    {
487      ((GenericDialog) w).setEnabledCancel(enable);
488    }
489    else if (w instanceof GenericFrame)
490    {
491      ((GenericFrame) w).setEnabledCancel(enable);
492    }
493    enableCancel = enable;
494  }
495
496  /**
497   * Updates the font type and color of the component to be invalid and primary.
498   *
499   * @param comp
500   *          the component to update.
501   */
502  protected void setPrimaryInvalid(final JComponent comp)
503  {
504    comp.setFont(ColorAndFontConstants.primaryInvalidFont);
505    comp.setForeground(ColorAndFontConstants.invalidFontColor);
506  }
507
508  /**
509   * Updates the font type and color of the component to be valid and primary.
510   *
511   * @param comp
512   *          the component to update.
513   */
514  protected void setPrimaryValid(final JComponent comp)
515  {
516    comp.setForeground(ColorAndFontConstants.validFontColor);
517    comp.setFont(ColorAndFontConstants.primaryFont);
518  }
519
520  /**
521   * Updates the font type and color of the component to be invalid and
522   * secondary.
523   *
524   * @param comp
525   *          the component to update.
526   */
527  protected void setSecondaryInvalid(final JComponent comp)
528  {
529    comp.setForeground(ColorAndFontConstants.invalidFontColor);
530    comp.setFont(ColorAndFontConstants.invalidFont);
531  }
532
533  /**
534   * Updates the font type and color of the component to be valid and secondary.
535   *
536   * @param comp
537   *          the component to update.
538   */
539  protected void setSecondaryValid(final JComponent comp)
540  {
541    comp.setForeground(ColorAndFontConstants.validFontColor);
542    comp.setFont(ColorAndFontConstants.defaultFont);
543  }
544
545  /**
546   * Packs the parent dialog.
547   */
548  protected void packParentDialog()
549  {
550    Window dlg = Utilities.getParentDialog(this);
551    if (dlg != null)
552    {
553      invalidate();
554      dlg.invalidate();
555      dlg.pack();
556      if (!SwingUtilities.isEventDispatchThread())
557      {
558        Thread.dumpStack();
559      }
560    }
561  }
562
563  /**
564   * Notification that the ok button has been clicked, the panel is in charge of
565   * doing whatever is required (close the dialog, launch a task, etc.).
566   */
567  public abstract void okClicked();
568
569  /**
570   * Adds a configuration element created listener.
571   *
572   * @param listener
573   *          the listener.
574   */
575  public void addConfigurationElementCreatedListener(final ConfigurationElementCreatedListener listener)
576  {
577    getConfigurationElementCreatedListeners().add(listener);
578  }
579
580  /**
581   * Removes a configuration element created listener.
582   *
583   * @param listener
584   *          the listener.
585   */
586  public void removeConfigurationElementCreatedListener(final ConfigurationElementCreatedListener listener)
587  {
588    getConfigurationElementCreatedListeners().remove(listener);
589  }
590
591  /**
592   * Returns the list of configuration listeners.
593   *
594   * @return the list of configuration listeners.
595   */
596  protected List<ConfigurationElementCreatedListener> getConfigurationElementCreatedListeners()
597  {
598    return confListeners;
599  }
600
601  /**
602   * Notification that cancel was clicked, the panel is in charge of doing
603   * whatever is required (close the dialog, etc.).
604   */
605  public void cancelClicked()
606  {
607    // Default implementation
608    Utilities.getParentDialog(this).setVisible(false);
609    if (isDisposeOnClose())
610    {
611      Utilities.getParentDialog(this).dispose();
612    }
613  }
614
615  /**
616   * Whether the dialog should be disposed when the user closes it.
617   *
618   * @return <CODE>true</CODE> if the dialog should be disposed when the user
619   *         closes it or <CODE>true</CODE> otherwise.
620   */
621  public boolean isDisposeOnClose()
622  {
623    return disposeOnClose;
624  }
625
626  /**
627   * Sets whether the dialog should be disposed when the user closes it or not.
628   *
629   * @param disposeOnClose
630   *          <CODE>true</CODE> if the dialog should be disposed when the user
631   *          closes it or <CODE>true</CODE> otherwise.
632   */
633  public void setDisposeOnClose(final boolean disposeOnClose)
634  {
635    this.disposeOnClose = disposeOnClose;
636  }
637
638  /**
639   * Notification that close was clicked, the panel is in charge of doing
640   * whatever is required (close the dialog, etc.).
641   */
642  public void closeClicked()
643  {
644    // Default implementation
645    Utilities.getParentDialog(this).setVisible(false);
646    if (isDisposeOnClose())
647    {
648      Utilities.getParentDialog(this).dispose();
649    }
650  }
651
652  /**
653   * Displays a dialog with the provided list of error messages.
654   *
655   * @param errors
656   *          the error messages.
657   */
658  protected void displayErrorDialog(final Collection<LocalizableMessage> errors)
659  {
660    Utilities.displayErrorDialog(Utilities.getParentDialog(this), errors);
661  }
662
663  /**
664   * Displays a confirmation message.
665   *
666   * @param title
667   *          the title/summary of the message.
668   * @param msg
669   *          the description of the confirmation.
670   * @return <CODE>true</CODE> if the user confirms and <CODE>false</CODE>
671   *         otherwise.
672   */
673  protected boolean displayConfirmationDialog(final LocalizableMessage title, final LocalizableMessage msg)
674  {
675    return Utilities.displayConfirmationDialog(Utilities.getParentDialog(this), title, msg);
676  }
677
678  /**
679   * If the index must be rebuilt, asks the user for confirmation. If the user
680   * confirms launches a task that will rebuild the indexes. The progress will
681   * be displayed in the provided progress dialog.
682   *
683   * @param index
684   *          the index.
685   * @param progressDialog
686   *          the progress dialog.
687   */
688  protected void rebuildIndexIfNecessary(final AbstractIndexDescriptor index, final ProgressDialog progressDialog)
689  {
690    progressDialog.setTaskIsOver(false);
691    boolean rebuildIndexes;
692    String backendName = index.getBackend().getBackendID();
693    LocalizableMessage summary = INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_SUMMARY.get();
694    if (!isServerRunning())
695    {
696      rebuildIndexes = Utilities.displayConfirmationDialog( progressDialog, summary,
697          INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_OFFLINE_DETAILS.get(index.getName(), backendName));
698    }
699    else if (isLocal())
700    {
701      rebuildIndexes = Utilities.displayConfirmationDialog(progressDialog, summary,
702          INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_ONLINE_DETAILS.get(index.getName(), backendName, backendName));
703    }
704    else
705    {
706      Utilities.displayWarningDialog(progressDialog, summary,
707          INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_REMOTE_DETAILS.get(index.getName(), backendName));
708      rebuildIndexes = false;
709    }
710    if (rebuildIndexes)
711    {
712      SortedSet<AbstractIndexDescriptor> indexes = new TreeSet<>();
713      indexes.add(index);
714      SortedSet<String> baseDNs = new TreeSet<>();
715      for (BaseDNDescriptor b : index.getBackend().getBaseDns())
716      {
717        baseDNs.add(Utilities.unescapeUtf8(b.getDn().toString()));
718      }
719
720      RebuildIndexTask newTask = new RebuildIndexTask(getInfo(), progressDialog, baseDNs, indexes);
721      List<LocalizableMessage> errors = new ArrayList<>();
722      for (Task task : getInfo().getTasks())
723      {
724        task.canLaunch(newTask, errors);
725      }
726      if (errors.isEmpty())
727      {
728        progressDialog.appendProgressHtml("<br><br>");
729        launchOperation(newTask, INFO_CTRL_PANEL_REBUILDING_INDEXES_SUMMARY.get(backendName),
730            INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_SUMMARY.get(),
731            INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_DETAILS.get(),
732            ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_SUMMARY.get(), null,
733            ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_DETAILS, progressDialog, false);
734        if (progressDialog.isModal())
735        {
736          progressDialog.toFront();
737        }
738        progressDialog.setVisible(true);
739        if (!progressDialog.isModal())
740        {
741          progressDialog.toFront();
742        }
743      }
744      if (!errors.isEmpty())
745      {
746        displayErrorDialog(errors);
747      }
748    }
749    else
750    {
751      progressDialog.setTaskIsOver(true);
752      if (progressDialog.isVisible())
753      {
754        progressDialog.toFront();
755      }
756    }
757  }
758
759  /**
760   * A class used to avoid the possibility a certain type of objects in a combo
761   * box. This is used for instance in the combo box that contains base DNs
762   * where the base DNs are separated in backends, so the combo box displays
763   * both the backends (~ categories) and base DNs (~ values) and we do not
764   * allow to select the backends (~ categories).
765   */
766  protected class IgnoreItemListener implements ItemListener
767  {
768    private Object selectedItem;
769    private final JComboBox combo;
770
771    /**
772     * Constructor.
773     *
774     * @param combo
775     *          the combo box.
776     */
777    public IgnoreItemListener(final JComboBox combo)
778    {
779      this.combo = combo;
780      selectedItem = combo.getSelectedItem();
781      if (isCategory(selectedItem))
782      {
783        selectedItem = null;
784      }
785    }
786
787    @Override
788    public void itemStateChanged(final ItemEvent ev)
789    {
790      Object o = combo.getSelectedItem();
791      if (isCategory(o))
792      {
793        if (selectedItem == null)
794        {
795          selectedItem = firstNonCategoryItem(combo.getModel());
796        }
797        if (selectedItem != null)
798        {
799          combo.setSelectedItem(selectedItem);
800        }
801      }
802      else if (COMBO_SEPARATOR.equals(o))
803      {
804        combo.setSelectedItem(selectedItem);
805      }
806      else
807      {
808        selectedItem = o;
809      }
810    }
811
812    private Object firstNonCategoryItem(ComboBoxModel model)
813    {
814      for (int i = 0; i < model.getSize(); i++)
815      {
816        Object item = model.getElementAt(i);
817        if (item instanceof CategorizedComboBoxElement && !isCategory(item))
818        {
819          return item;
820        }
821      }
822      return null;
823    }
824  }
825
826  /**
827   * Returns the HTML required to render an Authenticate button in HTML.
828   *
829   * @return the HTML required to render an Authenticate button in HTML.
830   */
831  protected String getAuthenticateHTML()
832  {
833    return "<INPUT type=\"submit\" value=\"" + AUTHENTICATE + "\"></INPUT>";
834  }
835
836  /**
837   * Returns the HTML required to render an Start button in HTML.
838   *
839   * @return the HTML required to render an Start button in HTML.
840   */
841  protected String getStartServerHTML()
842  {
843    return "<INPUT type=\"submit\" value=\"" + START + "\"></INPUT>";
844  }
845
846  /**
847   * Updates the error panel and enables/disables the OK button depending on the
848   * status of the server.
849   *
850   * @param desc
851   *          the Server Descriptor.
852   * @param details
853   *          the message to be displayed if authentication has not been
854   *          provided and the server is running.
855   */
856  protected void updateErrorPaneAndOKButtonIfAuthRequired(
857      final ServerDescriptor desc, final LocalizableMessage details)
858  {
859    if (authenticationRequired(desc))
860    {
861      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
862      mb.append(details);
863      mb.append("<br><br>").append(getAuthenticateHTML());
864      LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
865      updateErrorPane(
866          errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont);
867      SwingUtilities.invokeLater(new Runnable()
868      {
869        @Override
870        public void run()
871        {
872          errorPane.setVisible(true);
873          packParentDialog();
874          setEnabledOK(false);
875        }
876      });
877    }
878    else
879    {
880      SwingUtilities.invokeLater(new Runnable()
881      {
882        @Override
883        public void run()
884        {
885          errorPane.setVisible(false);
886          checkOKButtonEnable();
887        }
888      });
889    }
890  }
891
892  /**
893   * Returns <CODE>true</CODE> if the server is running and the user did not
894   * provide authentication and <CODE>false</CODE> otherwise.
895   *
896   * @param desc
897   *          the server descriptor.
898   * @return <CODE>true</CODE> if the server is running and the user did not
899   *         provide authentication and <CODE>false</CODE> otherwise.
900   */
901  protected boolean authenticationRequired(final ServerDescriptor desc)
902  {
903    ServerDescriptor.ServerStatus status = desc.getStatus();
904    return (status == ServerDescriptor.ServerStatus.STARTED && !desc.isAuthenticated())
905        || status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE;
906  }
907
908  /**
909   * Updates the error panel depending on the status of the server.
910   *
911   * @param desc
912   *          the Server Descriptor.
913   * @param details
914   *          the message to be displayed if authentication has not been
915   *          provided and the server is running.
916   */
917  protected void updateErrorPaneIfAuthRequired(final ServerDescriptor desc, final LocalizableMessage details)
918  {
919    if (authenticationRequired(desc))
920    {
921      LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
922      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
923      mb.append(details);
924      mb.append("<br><br>").append(getAuthenticateHTML());
925      updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(),
926          ColorAndFontConstants.defaultFont);
927      SwingUtilities.invokeLater(new Runnable()
928      {
929        @Override
930        public void run()
931        {
932          errorPane.setVisible(true);
933          packParentDialog();
934        }
935      });
936    }
937    else
938    {
939      SwingUtilities.invokeLater(new Runnable()
940      {
941        @Override
942        public void run()
943        {
944          errorPane.setVisible(false);
945        }
946      });
947    }
948  }
949
950  /**
951   * Updates the error panel depending on the status of the server. This method
952   * will display an error message in the error pane if the server is not
953   * running and another message if the server is running but authentication has
954   * not been provided.
955   *
956   * @param desc
957   *          the Server Descriptor.
958   * @param detailsServerNotRunning
959   *          the message to be displayed if the server is not running.
960   * @param authRequired
961   *          the message to be displayed if authentication has not been
962   *          provided and the server is running.
963   */
964  protected void updateErrorPaneIfServerRunningAndAuthRequired(final ServerDescriptor desc,
965      final LocalizableMessage detailsServerNotRunning, final LocalizableMessage authRequired)
966  {
967    ServerDescriptor.ServerStatus status = desc.getStatus();
968    if (status != ServerDescriptor.ServerStatus.STARTED
969        && status != ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE)
970    {
971      LocalizableMessage title = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get();
972      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
973      mb.append(detailsServerNotRunning);
974      mb.append("<br><br>").append(getStartServerHTML());
975      updateErrorPane(
976          errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont);
977      SwingUtilities.invokeLater(new Runnable()
978      {
979        /** {@inheritDoc} */
980        @Override
981        public void run()
982        {
983          errorPane.setVisible(true);
984          packParentDialog();
985        }
986      });
987    }
988    else if (authenticationRequired(desc))
989    {
990      LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
991      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
992      mb.append(authRequired);
993      mb.append("<br><br>").append(getAuthenticateHTML());
994      updateErrorPane(
995          errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont);
996      SwingUtilities.invokeLater(new Runnable()
997      {
998        @Override
999        public void run()
1000        {
1001          errorPane.setVisible(true);
1002          packParentDialog();
1003        }
1004      });
1005    }
1006    else
1007    {
1008      SwingUtilities.invokeLater(new Runnable()
1009      {
1010        @Override
1011        public void run()
1012        {
1013          errorPane.setVisible(false);
1014        }
1015      });
1016    }
1017  }
1018
1019  /**
1020   * Updates the enabling/disabling of the OK button. The code assumes that the
1021   * error pane has already been updated.
1022   */
1023  protected void checkOKButtonEnable()
1024  {
1025    setEnabledOK(!errorPane.isVisible());
1026  }
1027
1028  /**
1029   * Returns <CODE>true</CODE> if the provided object is a category object in a
1030   * combo box.
1031   *
1032   * @param o
1033   *          the item in the combo box.
1034   * @return <CODE>true</CODE> if the provided object is a category object in a
1035   *         combo box.
1036   */
1037  protected boolean isCategory(final Object o)
1038  {
1039    if (o instanceof CategorizedComboBoxElement)
1040    {
1041      CategorizedComboBoxElement desc = (CategorizedComboBoxElement) o;
1042      return desc.getType() == CategorizedComboBoxElement.Type.CATEGORY;
1043    }
1044    return false;
1045  }
1046
1047  /**
1048   * Returns the control panel info object.
1049   *
1050   * @return the control panel info object.
1051   */
1052  public ControlPanelInfo getInfo()
1053  {
1054    return info;
1055  }
1056
1057  /**
1058   * Sets the control panel info object.
1059   *
1060   * @param info
1061   *          the control panel info object.
1062   */
1063  public void setInfo(final ControlPanelInfo info)
1064  {
1065    if (!info.equals(this.info))
1066    {
1067      if (this.info != null)
1068      {
1069        this.info.removeConfigChangeListener(this);
1070      }
1071      this.info = info;
1072      this.info.addConfigChangeListener(this);
1073      if (SwingUtilities.isEventDispatchThread() && callConfigurationChangedInBackground())
1074      {
1075        final Color savedBackground = getBackground();
1076        setBackground(ColorAndFontConstants.background);
1077        if (!sizeSet)
1078        {
1079          setPreferredSize(mainPanel.getPreferredSize());
1080          sizeSet = true;
1081        }
1082        // Do it outside the event thread if the panel requires it.
1083        BackgroundTask<Void> worker = new BackgroundTask<Void>()
1084        {
1085          @Override
1086          public Void processBackgroundTask() throws Throwable
1087          {
1088            StaticUtils.sleep(1000);
1089            configurationChanged(new ConfigurationChangeEvent(StatusGenericPanel.this.info,
1090                StatusGenericPanel.this.info.getServerDescriptor()));
1091            return null;
1092          }
1093
1094          @Override
1095          public void backgroundTaskCompleted(final Void returnValue, final Throwable t)
1096          {
1097            setBackground(savedBackground);
1098            displayMainPanel();
1099            if (!focusSet)
1100            {
1101              focusSet = true;
1102              Component comp = getPreferredFocusComponent();
1103              if (comp != null)
1104              {
1105                comp.requestFocusInWindow();
1106              }
1107            }
1108          }
1109        };
1110        displayMessage(INFO_CTRL_PANEL_LOADING_PANEL_SUMMARY.get());
1111        worker.startBackgroundTask();
1112      }
1113      else if (info.getServerDescriptor() != null)
1114      {
1115        configurationChanged(new ConfigurationChangeEvent(this.info, this.info.getServerDescriptor()));
1116      }
1117    }
1118  }
1119
1120  /** Displays the main panel. */
1121  protected void displayMainPanel()
1122  {
1123    cardLayout.show(cardPanel, MAIN_PANEL);
1124  }
1125
1126  /**
1127   * Displays a message and hides the main panel.
1128   *
1129   * @param msg
1130   *          the message to be displayed.
1131   */
1132  protected void displayMessage(final LocalizableMessage msg)
1133  {
1134    message.setText(Utilities.applyFont(msg.toString(), ColorAndFontConstants.defaultFont));
1135    cardLayout.show(cardPanel, MESSAGE_PANEL);
1136    message.requestFocusInWindow();
1137  }
1138
1139  /**
1140   * Displays an error message and hides the main panel.
1141   *
1142   * @param title
1143   *          the title of the message to be displayed.
1144   * @param msg
1145   *          the message to be displayed.
1146   */
1147  protected void displayErrorMessage(final LocalizableMessage title, final LocalizableMessage msg)
1148  {
1149    updateErrorPane(message, title, ColorAndFontConstants.errorTitleFont, msg, ColorAndFontConstants.defaultFont);
1150    cardLayout.show(cardPanel, MESSAGE_PANEL);
1151    message.requestFocusInWindow();
1152  }
1153
1154  /**
1155   * Updates the contents of an editor pane using the error format.
1156   *
1157   * @param pane
1158   *          the editor pane to be updated.
1159   * @param title
1160   *          the title.
1161   * @param titleFont
1162   *          the font to be used for the title.
1163   * @param details
1164   *          the details message.
1165   * @param detailsFont
1166   *          the font to be used for the details.
1167   */
1168  protected void updateErrorPane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont,
1169      final LocalizableMessage details, final Font detailsFont)
1170  {
1171    updatePane(pane, title, titleFont, details, detailsFont, PanelType.ERROR);
1172  }
1173
1174  /**
1175   * Updates the contents of an editor pane using the confirmation format.
1176   *
1177   * @param pane
1178   *          the editor pane to be updated.
1179   * @param title
1180   *          the title.
1181   * @param titleFont
1182   *          the font to be used for the title.
1183   * @param details
1184   *          the details message.
1185   * @param detailsFont
1186   *          the font to be used for the details.
1187   */
1188  protected void updateConfirmationPane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont,
1189      final LocalizableMessage details, final Font detailsFont)
1190  {
1191    updatePane(pane, title, titleFont, details, detailsFont, PanelType.CONFIRMATION);
1192  }
1193
1194  /** The different types of error panels that are handled. */
1195  private enum PanelType
1196  {
1197    /** The message in the panel is an error. */
1198    ERROR,
1199    /** The message in the panel is a confirmation. */
1200    CONFIRMATION,
1201    /** The message in the panel is an information message. */
1202    INFORMATION,
1203    /** The message in the panel is a warning message. */
1204    WARNING
1205  }
1206
1207  /**
1208   * Updates the contents of an editor pane using the provided format.
1209   *
1210   * @param pane
1211   *          the editor pane to be updated.
1212   * @param title
1213   *          the title.
1214   * @param titleFont
1215   *          the font to be used for the title.
1216   * @param details
1217   *          the details message.
1218   * @param detailsFont
1219   *          the font to be used for the details.
1220   * @param type
1221   *          the type of panel.
1222   */
1223  private void updatePane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont,
1224      final LocalizableMessage details, final Font detailsFont, final PanelType type)
1225  {
1226    String text = getText(type, title, titleFont, details, detailsFont);
1227    if (!text.equals(lastDisplayedError))
1228    {
1229      LocalizableMessage wrappedTitle = Utilities.wrapHTML(title, 80);
1230      LocalizableMessage wrappedDetails = Utilities.wrapHTML(details, 90);
1231
1232      JEditorPane wrappedPane = Utilities.makeHtmlPane(null, pane.getFont());
1233      String wrappedText;
1234      switch (type)
1235      {
1236      case ERROR:
1237        wrappedText = Utilities.getFormattedError(wrappedTitle, titleFont, wrappedDetails, detailsFont);
1238        break;
1239      default:
1240        wrappedText = Utilities.getFormattedSuccess(wrappedTitle, titleFont, wrappedDetails, detailsFont);
1241        break;
1242      }
1243      wrappedPane.setText(wrappedText);
1244      Dimension d = wrappedPane.getPreferredSize();
1245
1246      pane.setText(text);
1247      pane.setPreferredSize(d);
1248
1249      lastDisplayedError = text;
1250    }
1251    final Window window = Utilities.getParentDialog(StatusGenericPanel.this);
1252    if (window != null)
1253    {
1254      SwingUtilities.invokeLater(new Runnable()
1255      {
1256        @Override
1257        public void run()
1258        {
1259          pane.invalidate();
1260          window.validate();
1261        }
1262      });
1263    }
1264  }
1265
1266  private String getText(
1267      PanelType type, LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont)
1268  {
1269    switch (type)
1270    {
1271    case ERROR:
1272      return Utilities.getFormattedError(title, titleFont, details, detailsFont);
1273    case CONFIRMATION:
1274      return Utilities.getFormattedConfirmation(title, titleFont, details, detailsFont);
1275    case WARNING:
1276      return Utilities.getFormattedWarning(title, titleFont, details, detailsFont);
1277    default:
1278      return Utilities.getFormattedSuccess(title, titleFont, details, detailsFont);
1279    }
1280  }
1281
1282  /**
1283   * Commodity method used to update the elements of a combo box that contains
1284   * the different user backends. If no backends are found the combo box will be
1285   * made invisible and a label will be made visible. This method does not
1286   * update the label's text nor creates any layout.
1287   *
1288   * @param combo
1289   *          the combo to be updated.
1290   * @param lNoBackendsFound
1291   *          the label that must be shown if no user backends are found.
1292   * @param desc
1293   *          the server descriptor that contains the configuration.
1294   */
1295  protected void updateSimpleBackendComboBoxModel(final JComboBox combo, final JLabel lNoBackendsFound,
1296      final ServerDescriptor desc)
1297  {
1298    final SortedSet<String> newElements = new TreeSet<>(new LowerCaseComparator());
1299    for (BackendDescriptor backend : desc.getBackends())
1300    {
1301      if (!backend.isConfigBackend())
1302      {
1303        newElements.add(backend.getBackendID());
1304      }
1305    }
1306    DefaultComboBoxModel model = (DefaultComboBoxModel) combo.getModel();
1307    updateComboBoxModel(newElements, model);
1308    SwingUtilities.invokeLater(new Runnable()
1309    {
1310      @Override
1311      public void run()
1312      {
1313        boolean noElems = newElements.isEmpty();
1314        combo.setVisible(!noElems);
1315        lNoBackendsFound.setVisible(noElems);
1316      }
1317    });
1318  }
1319
1320  /**
1321   * Method that says if a backend must be displayed. Only non-config backends
1322   * are displayed.
1323   *
1324   * @param backend
1325   *          the backend.
1326   * @return <CODE>true</CODE> if the backend must be displayed and
1327   *         <CODE>false</CODE> otherwise.
1328   */
1329  protected boolean displayBackend(final BackendDescriptor backend)
1330  {
1331    return !backend.isConfigBackend();
1332  }
1333
1334  /**
1335   * Commodity method to update a combo box model with the backends of a server.
1336   *
1337   * @param model
1338   *          the combo box model to be updated.
1339   * @param desc
1340   *          the server descriptor containing the configuration.
1341   */
1342  protected void updateBaseDNComboBoxModel(final DefaultComboBoxModel model, final ServerDescriptor desc)
1343  {
1344    Set<CategorizedComboBoxElement> newElements = new LinkedHashSet<>();
1345    SortedSet<String> backendIDs = new TreeSet<>(new LowerCaseComparator());
1346    Map<String, SortedSet<String>> hmBaseDNs = new HashMap<>();
1347
1348    for (BackendDescriptor backend : desc.getBackends())
1349    {
1350      if (displayBackend(backend))
1351      {
1352        String backendID = backend.getBackendID();
1353        backendIDs.add(backendID);
1354        SortedSet<String> baseDNs = new TreeSet<>(new LowerCaseComparator());
1355        for (BaseDNDescriptor baseDN : backend.getBaseDns())
1356        {
1357          try
1358          {
1359            baseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString()));
1360          }
1361          catch (Throwable t)
1362          {
1363            throw new RuntimeException("Unexpected error: " + t, t);
1364          }
1365        }
1366        hmBaseDNs.put(backendID, baseDNs);
1367      }
1368    }
1369
1370    for (String backendID : backendIDs)
1371    {
1372      newElements.add(new CategorizedComboBoxElement(backendID, CategorizedComboBoxElement.Type.CATEGORY));
1373      SortedSet<String> baseDNs = hmBaseDNs.get(backendID);
1374      for (String baseDN : baseDNs)
1375      {
1376        newElements.add(new CategorizedComboBoxElement(baseDN, CategorizedComboBoxElement.Type.REGULAR));
1377      }
1378    }
1379    updateComboBoxModel(newElements, model);
1380  }
1381
1382  /**
1383   * Updates a combo box model with a number of items.
1384   *
1385   * @param newElements
1386   *          the new items for the combo box model.
1387   * @param model
1388   *          the combo box model to be updated.
1389   */
1390  protected void updateComboBoxModel(final Collection<?> newElements, final DefaultComboBoxModel model)
1391  {
1392    updateComboBoxModel(newElements, model, null);
1393  }
1394
1395  /**
1396   * Updates a combo box model with a number of items. The method assumes that
1397   * is called outside the event thread.
1398   *
1399   * @param newElements
1400   *          the new items for the combo box model.
1401   * @param model
1402   *          the combo box model to be updated.
1403   * @param comparator
1404   *          the object that will be used to compare the objects in the model.
1405   *          If <CODE>null</CODE>, the equals method will be used.
1406   */
1407  private void updateComboBoxModel(final Collection<?> newElements, final DefaultComboBoxModel model,
1408      final Comparator<Object> comparator)
1409  {
1410    SwingUtilities.invokeLater(new Runnable()
1411    {
1412      @Override
1413      public void run()
1414      {
1415        Utilities.updateComboBoxModel(newElements, model, comparator);
1416      }
1417    });
1418  }
1419
1420  /**
1421   * Updates a map, so that the keys are the base DN where the indexes are
1422   * defined and the values are a sorted set of indexes.
1423   *
1424   * @param desc
1425   *          the server descriptor containing the index configuration.
1426   * @param hmIndexes
1427   *          the map to be updated.
1428   */
1429  protected void updateIndexMap(
1430      final ServerDescriptor desc, final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes)
1431  {
1432    synchronized (hmIndexes)
1433    {
1434      Set<String> dns = new HashSet<>();
1435      for (BackendDescriptor backend : desc.getBackends())
1436      {
1437        if (backend.getType() == BackendDescriptor.Type.PLUGGABLE)
1438        {
1439          for (BaseDNDescriptor baseDN : backend.getBaseDns())
1440          {
1441            String dn;
1442            try
1443            {
1444              dn = Utilities.unescapeUtf8(baseDN.getDn().toString());
1445            }
1446            catch (Throwable t)
1447            {
1448              throw new RuntimeException("Unexpected error: " + t, t);
1449            }
1450            dns.add(dn);
1451            SortedSet<AbstractIndexDescriptor> indexes = new TreeSet<AbstractIndexDescriptor>(backend.getIndexes());
1452            indexes.addAll(backend.getVLVIndexes());
1453            SortedSet<AbstractIndexDescriptor> currentIndexes = hmIndexes.get(dn);
1454            if (currentIndexes != null)
1455            {
1456              if (!currentIndexes.equals(indexes))
1457              {
1458                hmIndexes.put(dn, indexes);
1459              }
1460            }
1461            else
1462            {
1463              hmIndexes.put(dn, indexes);
1464            }
1465          }
1466        }
1467      }
1468      for (String dn : new HashSet<String>(hmIndexes.keySet()))
1469      {
1470        if (!dns.contains(dn))
1471        {
1472          hmIndexes.remove(dn);
1473        }
1474      }
1475    }
1476  }
1477
1478  /**
1479   * Updates and addremove panel with the contents of the provided item. The
1480   * selected item represents a base DN.
1481   *
1482   * @param hmIndexes
1483   *          the map that contains the indexes definitions as values and the
1484   *          base DNs as keys.
1485   * @param selectedItem
1486   *          the selected item.
1487   * @param addRemove
1488   *          the add remove panel to be updated.
1489   */
1490  protected void comboBoxSelected(final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes,
1491      final CategorizedComboBoxElement selectedItem, final AddRemovePanel<AbstractIndexDescriptor> addRemove)
1492  {
1493    synchronized (hmIndexes)
1494    {
1495      String selectedDn = null;
1496      if (selectedItem != null)
1497      {
1498        selectedDn = (String) selectedItem.getValue();
1499      }
1500      if (selectedDn != null)
1501      {
1502        SortedSet<AbstractIndexDescriptor> indexes = hmIndexes.get(selectedDn);
1503        if (indexes != null)
1504        {
1505          boolean availableChanged = false;
1506          boolean selectedChanged = false;
1507          SortableListModel<AbstractIndexDescriptor> availableListModel = addRemove.getAvailableListModel();
1508          SortableListModel<AbstractIndexDescriptor> selectedListModel = addRemove.getSelectedListModel();
1509          SortedSet<AbstractIndexDescriptor> availableIndexes = availableListModel.getData();
1510          SortedSet<AbstractIndexDescriptor> selectedIndexes = selectedListModel.getData();
1511          availableChanged = availableIndexes.retainAll(indexes);
1512          selectedChanged = selectedIndexes.retainAll(indexes);
1513
1514          for (AbstractIndexDescriptor index : indexes)
1515          {
1516            if (!availableIndexes.contains(index) && !selectedIndexes.contains(index))
1517            {
1518              availableIndexes.add(index);
1519              availableChanged = true;
1520            }
1521          }
1522          if (availableChanged)
1523          {
1524            availableListModel.clear();
1525            availableListModel.addAll(availableIndexes);
1526            availableListModel.fireContentsChanged(availableListModel, 0, availableListModel.getSize());
1527          }
1528          if (selectedChanged)
1529          {
1530            selectedListModel.clear();
1531            selectedListModel.addAll(selectedIndexes);
1532            selectedListModel.fireContentsChanged(selectedListModel, 0, selectedListModel.getSize());
1533          }
1534        }
1535      }
1536    }
1537  }
1538
1539  /**
1540   * Returns <CODE>true</CODE> if the cancel button is enabled and
1541   * <CODE>false</CODE> otherwise.
1542   *
1543   * @return <CODE>true</CODE> if the cancel button is enabled and
1544   *         <CODE>false</CODE> otherwise.
1545   */
1546  public boolean isEnableCancel()
1547  {
1548    return enableCancel;
1549  }
1550
1551  /**
1552   * Returns <CODE>true</CODE> if the close button is enabled and
1553   * <CODE>false</CODE> otherwise.
1554   *
1555   * @return <CODE>true</CODE> if the close button is enabled and
1556   *         <CODE>false</CODE> otherwise.
1557   */
1558  public boolean isEnableClose()
1559  {
1560    return enableClose;
1561  }
1562
1563  /**
1564   * Returns <CODE>true</CODE> if the ok button is enabled and
1565   * <CODE>false</CODE> otherwise.
1566   *
1567   * @return <CODE>true</CODE> if the ok button is enabled and
1568   *         <CODE>false</CODE> otherwise.
1569   */
1570  public boolean isEnableOK()
1571  {
1572    return enableOK;
1573  }
1574
1575  /**
1576   * Returns <CODE>true</CODE> if the server is running and <CODE>false</CODE>
1577   * otherwise.
1578   *
1579   * @return <CODE>true</CODE> if the server is running and <CODE>false</CODE>
1580   *         otherwise.
1581   */
1582  protected boolean isServerRunning()
1583  {
1584    return getInfo().getServerDescriptor().getStatus() == ServerDescriptor.ServerStatus.STARTED;
1585  }
1586
1587  /**
1588   * Returns <CODE>true</CODE> if the managed server is the local installation
1589   * (where the control panel is installed) <CODE>false</CODE> otherwise.
1590   *
1591   * @return <CODE>true</CODE> if the managed server is the local installation
1592   *         (where the control panel is installed) <CODE>false</CODE>
1593   *         otherwise.
1594   */
1595  protected boolean isLocal()
1596  {
1597    return getInfo().getServerDescriptor().isLocal();
1598  }
1599
1600  /**
1601   * Launch an task.
1602   *
1603   * @param task
1604   *          the task to be launched.
1605   * @param initialSummary
1606   *          the initial summary to be displayed in the progress dialog.
1607   * @param successSummary
1608   *          the success summary to be displayed in the progress dialog if the
1609   *          task is successful.
1610   * @param successDetail
1611   *          the success details to be displayed in the progress dialog if the
1612   *          task is successful.
1613   * @param errorSummary
1614   *          the error summary to be displayed in the progress dialog if the
1615   *          task ended with error.
1616   * @param errorDetail
1617   *          error details to be displayed in the progress dialog if the task
1618   *          ended with error.
1619   * @param errorDetailCode
1620   *          error detail message to be displayed in the progress dialog if the
1621   *          task ended with error and we have an exit error code (for instance
1622   *          if the error occurred when launching a script we will have an
1623   *          error code).
1624   * @param dialog
1625   *          the progress dialog.
1626   */
1627  protected void launchOperation(final Task task, final LocalizableMessage initialSummary,
1628      final LocalizableMessage successSummary, final LocalizableMessage successDetail,
1629      final LocalizableMessage errorSummary, final LocalizableMessage errorDetail,
1630      final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog)
1631  {
1632    launchOperation(task, initialSummary, successSummary, successDetail, errorSummary, errorDetail, errorDetailCode,
1633        dialog, true);
1634  }
1635
1636  /**
1637   * Launch an task.
1638   *
1639   * @param task
1640   *          the task to be launched.
1641   * @param initialSummary
1642   *          the initial summary to be displayed in the progress dialog.
1643   * @param successSummary
1644   *          the success summary to be displayed in the progress dialog if the
1645   *          task is successful.
1646   * @param successDetail
1647   *          the success details to be displayed in the progress dialog if the
1648   *          task is successful.
1649   * @param errorSummary
1650   *          the error summary to be displayed in the progress dialog if the
1651   *          task ended with error.
1652   * @param errorDetail
1653   *          error details to be displayed in the progress dialog if the task
1654   *          ended with error.
1655   * @param errorDetailCode
1656   *          error detail message to be displayed in the progress dialog if the
1657   *          task ended with error and we have an exit error code (for instance
1658   *          if the error occurred when launching a script we will have an
1659   *          error code).
1660   * @param dialog
1661   *          the progress dialog.
1662   * @param resetLogs
1663   *          whether the contents of the progress dialog should be reset or
1664   *          not.
1665   */
1666  private void launchOperation(final Task task, final LocalizableMessage initialSummary,
1667      final LocalizableMessage successSummary, final LocalizableMessage successDetail,
1668      final LocalizableMessage errorSummary, final LocalizableMessage errorDetail,
1669      final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog,
1670      final boolean resetLogs)
1671  {
1672    launchOperation(task, initialSummary, successSummary, successDetail, errorSummary, errorDetail, errorDetailCode,
1673        dialog, resetLogs, getInfo());
1674  }
1675
1676  /**
1677   * Launch an task.
1678   *
1679   * @param task
1680   *          the task to be launched.
1681   * @param initialSummary
1682   *          the initial summary to be displayed in the progress dialog.
1683   * @param successSummary
1684   *          the success summary to be displayed in the progress dialog if the
1685   *          task is successful.
1686   * @param successDetail
1687   *          the success details to be displayed in the progress dialog if the
1688   *          task is successful.
1689   * @param errorSummary
1690   *          the error summary to be displayed in the progress dialog if the
1691   *          task ended with error.
1692   * @param errorDetail
1693   *          error details to be displayed in the progress dialog if the task
1694   *          ended with error.
1695   * @param errorDetailCode
1696   *          error detail message to be displayed in the progress dialog if the
1697   *          task ended with error and we have an exit error code (for instance
1698   *          if the error occurred when launching a script we will have an
1699   *          error code).
1700   * @param dialog
1701   *          the progress dialog.
1702   * @param resetLogs
1703   *          whether the contents of the progress dialog should be reset or
1704   *          not.
1705   * @param info
1706   *          the ControlPanelInfo.
1707   */
1708  public static void launchOperation(final Task task, final LocalizableMessage initialSummary,
1709      final LocalizableMessage successSummary, final LocalizableMessage successDetail,
1710      final LocalizableMessage errorSummary, final LocalizableMessage errorDetail,
1711      final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog,
1712      final boolean resetLogs, final ControlPanelInfo info)
1713  {
1714    dialog.setTaskIsOver(false);
1715    dialog.getProgressBar().setIndeterminate(true);
1716    dialog.addPrintStreamListeners(task.getOutPrintStream(), task.getErrorPrintStream());
1717    if (resetLogs)
1718    {
1719      dialog.resetProgressLogs();
1720    }
1721    String cmdLine = task.getCommandLineToDisplay();
1722    if (cmdLine != null)
1723    {
1724      dialog.appendProgressHtml(Utilities.applyFont(INFO_CTRL_PANEL_EQUIVALENT_COMMAND_LINE.get() + "<br><b>" + cmdLine
1725          + "</b><br><br>", ColorAndFontConstants.progressFont));
1726    }
1727    dialog.setEnabledClose(false);
1728    dialog.setSummary(LocalizableMessage.raw(Utilities.applyFont(initialSummary.toString(),
1729        ColorAndFontConstants.defaultFont)));
1730    dialog.getProgressBar().setVisible(true);
1731    BackgroundTask<Task> worker = new BackgroundTask<Task>()
1732    {
1733      @Override
1734      public Task processBackgroundTask() throws Throwable
1735      {
1736        task.runTask();
1737        if (task.regenerateDescriptor())
1738        {
1739          info.regenerateDescriptor();
1740        }
1741        return task;
1742      }
1743
1744      @Override
1745      public void backgroundTaskCompleted(final Task returnValue, Throwable t)
1746      {
1747        String summaryMsg;
1748        if (task.getState() == Task.State.FINISHED_SUCCESSFULLY)
1749        {
1750          summaryMsg =
1751              Utilities.getFormattedSuccess(successSummary, ColorAndFontConstants.errorTitleFont, successDetail,
1752                  ColorAndFontConstants.defaultFont);
1753        }
1754        else
1755        {
1756          if (t == null)
1757          {
1758            t = task.getLastException();
1759          }
1760
1761          if (t != null)
1762          {
1763            logger.warn(LocalizableMessage.raw("Error occurred running task: " + t, t));
1764            if (task.getReturnCode() != null && errorDetailCode != null)
1765            {
1766              String sThrowable;
1767              if (t instanceof OpenDsException)
1768              {
1769                sThrowable = ((OpenDsException) t).getMessageObject().toString();
1770              }
1771              else if (t.getMessage() != null)
1772              {
1773                sThrowable = t.getMessage();
1774              }
1775              else
1776              {
1777                sThrowable = t.toString();
1778              }
1779              LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
1780              mb.append(errorDetailCode.get(task.getReturnCode()));
1781              mb.append("  ").append(INFO_CTRL_PANEL_DETAILS_THROWABLE.get(sThrowable));
1782              summaryMsg =
1783                  Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, mb.toMessage(),
1784                      ColorAndFontConstants.defaultFont);
1785            }
1786            else if (errorDetail != null)
1787            {
1788              LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
1789              mb.append(errorDetail);
1790              mb.append(INFO_CTRL_PANEL_DETAILS_THROWABLE.get(t));
1791              summaryMsg =
1792                  Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, mb.toMessage(),
1793                      ColorAndFontConstants.defaultFont);
1794            }
1795            else
1796            {
1797              summaryMsg = null;
1798            }
1799          }
1800          else if (task.getReturnCode() != null && errorDetailCode != null)
1801          {
1802            summaryMsg =
1803                Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, errorDetailCode
1804                    .get(task.getReturnCode()), ColorAndFontConstants.defaultFont);
1805          }
1806          else if (errorDetail != null)
1807          {
1808            summaryMsg =
1809                Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, errorDetail,
1810                    ColorAndFontConstants.defaultFont);
1811          }
1812          else
1813          {
1814            summaryMsg = null;
1815          }
1816        }
1817        if (summaryMsg != null)
1818        {
1819          dialog.setSummary(LocalizableMessage.raw(summaryMsg));
1820        }
1821        dialog.setEnabledClose(true);
1822        dialog.getProgressBar().setVisible(false);
1823        if (task.getState() == Task.State.FINISHED_SUCCESSFULLY)
1824        {
1825          dialog.setTaskIsOver(true);
1826        }
1827        task.postOperation();
1828      }
1829    };
1830    info.registerTask(task);
1831    worker.startBackgroundTask();
1832  }
1833
1834  /**
1835   * Checks that the provided string value is a valid integer and if it is not
1836   * updates a list of error messages with an error.
1837   *
1838   * @param errors
1839   *          the list of error messages to be updated.
1840   * @param stringValue
1841   *          the string value to analyze.
1842   * @param minValue
1843   *          the minimum integer value accepted.
1844   * @param maxValue
1845   *          the maximum integer value accepted.
1846   * @param errMsg
1847   *          the error message to use to update the error list if the provided
1848   *          value is not valid.
1849   * @return {@code true} if the provided string value is a valid integer and if
1850   *         it is not updates a list of error messages with an error.
1851   */
1852  protected boolean checkIntValue(final Collection<LocalizableMessage> errors, final String stringValue,
1853      final int minValue, final int maxValue, final LocalizableMessage errMsg)
1854  {
1855    try
1856    {
1857      int n = Integer.parseInt(stringValue);
1858      if (minValue <= n && n <= maxValue)
1859      {
1860        return true;
1861      }
1862    }
1863    catch (NumberFormatException ignored)
1864    {
1865    }
1866
1867    errors.add(errMsg);
1868    return false;
1869  }
1870
1871  /**
1872   * Starts the server. This method will launch a task and open a progress
1873   * dialog that will start the server. This method must be called from the
1874   * event thread.
1875   */
1876  protected void startServer()
1877  {
1878    Set<LocalizableMessage> errors = new LinkedHashSet<>();
1879    ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this),
1880            INFO_CTRL_PANEL_START_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
1881    StartServerTask newTask = new StartServerTask(getInfo(), progressDialog);
1882    for (Task task : getInfo().getTasks())
1883    {
1884      task.canLaunch(newTask, errors);
1885    }
1886    if (errors.isEmpty())
1887    {
1888      launchOperation(newTask,
1889          INFO_CTRL_PANEL_STARTING_SERVER_SUMMARY.get(),
1890          INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_SUMMARY.get(),
1891          INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_DETAILS.get(),
1892          ERR_CTRL_PANEL_STARTING_SERVER_ERROR_SUMMARY.get(), null,
1893          ERR_CTRL_PANEL_STARTING_SERVER_ERROR_DETAILS, progressDialog);
1894      progressDialog.setVisible(true);
1895    }
1896    else
1897    {
1898      displayErrorDialog(errors);
1899    }
1900  }
1901
1902  /**
1903   * Stops the server. This method will launch a task and open a progress dialog
1904   * that will stop the server. This method must be called from the event
1905   * thread.
1906   */
1907  protected void stopServer()
1908  {
1909    Set<LocalizableMessage> errors = new LinkedHashSet<>();
1910    ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this),
1911            INFO_CTRL_PANEL_STOP_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
1912    StopServerTask newTask = new StopServerTask(getInfo(), progressDialog);
1913    for (Task task : getInfo().getTasks())
1914    {
1915      task.canLaunch(newTask, errors);
1916    }
1917    boolean confirmed = true;
1918    if (errors.isEmpty())
1919    {
1920      confirmed = displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
1921                                            INFO_CTRL_PANEL_CONFIRM_STOP_SERVER_DETAILS.get());
1922    }
1923    if (errors.isEmpty() && confirmed)
1924    {
1925      launchOperation(newTask,
1926          INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(),
1927          INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_SUMMARY.get(),
1928          INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_DETAILS.get(),
1929          ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_SUMMARY.get(), null,
1930          ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_DETAILS, progressDialog);
1931      progressDialog.setVisible(true);
1932    }
1933    if (!errors.isEmpty())
1934    {
1935      displayErrorDialog(errors);
1936    }
1937  }
1938
1939  /**
1940   * Restarts the server. This method will launch a task and open a progress
1941   * dialog that will restart the server. This method must be called from the
1942   * event thread.
1943   */
1944  protected void restartServer()
1945  {
1946    Set<LocalizableMessage> errors = new LinkedHashSet<>();
1947    ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this),
1948            INFO_CTRL_PANEL_RESTART_SERVER_PROGRESS_DLG_TITLE.get(), getInfo());
1949    RestartServerTask newTask = new RestartServerTask(getInfo(), progressDialog);
1950    for (Task task : getInfo().getTasks())
1951    {
1952      task.canLaunch(newTask, errors);
1953    }
1954    boolean confirmed = true;
1955    if (errors.isEmpty())
1956    {
1957      confirmed = displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
1958                                            INFO_CTRL_PANEL_CONFIRM_RESTART_SERVER_DETAILS.get());
1959    }
1960    if (errors.isEmpty() && confirmed)
1961    {
1962      launchOperation(newTask,
1963          INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(),
1964          INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_SUMMARY.get(),
1965          INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_DETAILS.get(),
1966          ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_SUMMARY.get(), null,
1967          ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_DETAILS, progressDialog);
1968      progressDialog.setVisible(true);
1969    }
1970    if (!errors.isEmpty())
1971    {
1972      displayErrorDialog(errors);
1973    }
1974  }
1975
1976  /**
1977   * Displays a dialog asking for authentication. This method must be called
1978   * from the event thread.
1979   */
1980  private void authenticate()
1981  {
1982    if (!getLoginDialog().isVisible())
1983    {
1984      getLoginDialog().setVisible(true);
1985    }
1986    getLoginDialog().toFront();
1987  }
1988
1989  /**
1990   * Returns the login dialog that is displayed when the method authenticate is
1991   * called.
1992   *
1993   * @return the login dialog that is displayed when the method authenticate is
1994   *         called.
1995   */
1996  protected GenericDialog getLoginDialog()
1997  {
1998    GenericDialog dialog = isLocal() ? getLocalServerLoginDialog(getInfo()) : getLocalOrRemoteDialog(getInfo());
1999    Utilities.centerGoldenMean(dialog, Utilities.getFrame(this));
2000    dialog.setModal(true);
2001    return dialog;
2002  }
2003
2004  /**
2005   * Tells whether an entry exists or not. Actually it tells if we could find a
2006   * given entry or not.
2007   *
2008   * @param dn
2009   *          the DN of the entry to look for.
2010   * @return <CODE>true</CODE> if the entry with the provided DN could be found
2011   *         and <CODE>false</CODE> otherwise.
2012   */
2013  protected boolean entryExists(final String dn)
2014  {
2015    boolean entryExists = false;
2016    try
2017    {
2018      SearchControls ctls = new SearchControls();
2019      ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
2020      ctls.setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES });
2021      String filter = BrowserController.ALL_OBJECTS_FILTER;
2022      NamingEnumeration<SearchResult> result =
2023          getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls);
2024
2025      try
2026      {
2027        while (result.hasMore())
2028        {
2029          SearchResult sr = result.next();
2030          entryExists = sr != null;
2031        }
2032      }
2033      finally
2034      {
2035        result.close();
2036      }
2037    }
2038    catch (Throwable t)
2039    {
2040    }
2041    return entryExists;
2042  }
2043
2044  /**
2045   * Tells whether a given entry exists and contains one of the specified object
2046   * classes.
2047   *
2048   * @param dn
2049   *          the DN of the entry.
2050   * @param objectClasses
2051   *          the object classes to check.
2052   * @return <CODE>true</CODE> if the entry exists and contains one of the
2053   *         specified object classes and <CODE>false</CODE> otherwise.
2054   */
2055  protected boolean hasObjectClass(final String dn, final String... objectClasses)
2056  {
2057    try
2058    {
2059      SearchControls ctls = new SearchControls();
2060      ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
2061      ctls.setReturningAttributes(new String[] { ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME });
2062      String filter = BrowserController.ALL_OBJECTS_FILTER;
2063      NamingEnumeration<SearchResult> result =
2064          getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls);
2065
2066      try
2067      {
2068        while (result.hasMore())
2069        {
2070          SearchResult sr = result.next();
2071          Set<String> values = ConnectionUtils.getValues(sr, ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
2072          if (values != null)
2073          {
2074            for (String s : values)
2075            {
2076              for (String objectClass : objectClasses)
2077              {
2078                if (s.equalsIgnoreCase(objectClass))
2079                {
2080                  return true;
2081                }
2082              }
2083            }
2084          }
2085        }
2086      }
2087      finally
2088      {
2089        result.close();
2090      }
2091    }
2092    catch (Throwable t)
2093    {
2094    }
2095    return false;
2096  }
2097
2098  /**
2099   * Returns the border to be used in the right panel of the dialog with a tree
2100   * on the left (for instance the schema browser, entry browser and index
2101   * browser).
2102   *
2103   * @return the border to be used in the right panel.
2104   */
2105  protected Border getRightPanelBorder()
2106  {
2107    return ColorAndFontConstants.textAreaBorder;
2108  }
2109
2110  /**
2111   * Returns the monitoring value in a String form to be displayed to the user.
2112   *
2113   * @param attr
2114   *          the attribute to analyze.
2115   * @param monitoringEntry
2116   *          the monitoring entry.
2117   * @return the monitoring value in a String form to be displayed to the user.
2118   */
2119  public static String getMonitoringValue(final MonitoringAttributes attr, final CustomSearchResult monitoringEntry)
2120  {
2121    return Utilities.getMonitoringValue(attr, monitoringEntry);
2122  }
2123
2124  /**
2125   * Updates the monitoring information writing it to a list of labels.
2126   *
2127   * @param monitoringAttrs
2128   *          the monitoring operations whose information we want to update.
2129   * @param monitoringLabels
2130   *          the monitoring labels to be updated.
2131   * @param monitoringEntry
2132   *          the monitoring entry containing the information to be displayed.
2133   */
2134  protected void updateMonitoringInfo(final List<MonitoringAttributes> monitoringAttrs,
2135      final List<JLabel> monitoringLabels, final CustomSearchResult monitoringEntry)
2136  {
2137    for (int i = 0; i < monitoringAttrs.size(); i++)
2138    {
2139      String value = getMonitoringValue(monitoringAttrs.get(i), monitoringEntry);
2140      JLabel l = monitoringLabels.get(i);
2141      l.setText(value);
2142    }
2143  }
2144
2145  /**
2146   * Returns the label to be used in panels (with ':') based on the definition
2147   * of the monitoring attribute.
2148   *
2149   * @param attr
2150   *          the monitoring attribute.
2151   * @return the label to be used in panels (with ':') based on the definition
2152   *         of the monitoring attribute.
2153   */
2154  protected static LocalizableMessage getLabel(final MonitoringAttributes attr)
2155  {
2156    return INFO_CTRL_PANEL_OPERATION_NAME_AS_LABEL.get(attr.getMessage());
2157  }
2158
2159  /**
2160   * Returns the command-line arguments associated with the provided schedule.
2161   *
2162   * @param schedule
2163   *          the schedule.
2164   * @return the command-line arguments associated with the provided schedule.
2165   */
2166  protected List<String> getScheduleArgs(final ScheduleType schedule)
2167  {
2168    List<String> args = new ArrayList<>(2);
2169    switch (schedule.getType())
2170    {
2171    case LAUNCH_LATER:
2172      args.add("--start");
2173      args.add(getStartTimeForTask(schedule.getLaunchLaterDate()));
2174      break;
2175    case LAUNCH_PERIODICALLY:
2176      args.add("--recurringTask");
2177      args.add(schedule.getCronValue());
2178      break;
2179    }
2180    return args;
2181  }
2182
2183  /**
2184   * Checks whether the server is running or not and depending on the schedule
2185   * updates the list of errors with the errors found.
2186   *
2187   * @param schedule
2188   *          the schedule.
2189   * @param errors
2190   *          the list of errors.
2191   * @param label
2192   *          the label to be marked as invalid if errors where encountered.
2193   */
2194  protected void addScheduleErrors(final ScheduleType schedule, final Collection<LocalizableMessage> errors,
2195      final JLabel label)
2196  {
2197    if (!isServerRunning())
2198    {
2199      ScheduleType.Type type = schedule.getType();
2200      if (type == ScheduleType.Type.LAUNCH_LATER)
2201      {
2202        errors.add(ERR_CTRL_PANEL_LAUNCH_LATER_REQUIRES_SERVER_RUNNING.get());
2203        setPrimaryInvalid(label);
2204      }
2205      else if (type == ScheduleType.Type.LAUNCH_PERIODICALLY)
2206      {
2207        errors.add(ERR_CTRL_PANEL_LAUNCH_SCHEDULE_REQUIRES_SERVER_RUNNING.get());
2208        setPrimaryInvalid(label);
2209      }
2210    }
2211  }
2212
2213  private String getStartTimeForTask(final Date date)
2214  {
2215    return taskDateFormat.format(date);
2216  }
2217
2218  /**
2219   * Checks whether the provided superior object classes are compatible with the
2220   * provided object class type. If not, the method updates the provided list of
2221   * error messages with a message describing the incompatibility.
2222   *
2223   * @param objectClassSuperiors
2224   *          the superior object classes.
2225   * @param objectClassType
2226   *          the object class type.
2227   * @param errors
2228   *          the list of error messages.
2229   */
2230  protected void checkCompatibleSuperiors(final Set<ObjectClass> objectClassSuperiors,
2231      final ObjectClassType objectClassType, final List<LocalizableMessage> errors)
2232  {
2233    SortedSet<String> notCompatibleClasses = new TreeSet<>(new LowerCaseComparator());
2234    for (ObjectClass oc : objectClassSuperiors)
2235    {
2236      if (oc.getObjectClassType() == ObjectClassType.ABSTRACT)
2237      {
2238        // Nothing to do.
2239      }
2240      else if (oc.getObjectClassType() != objectClassType)
2241      {
2242        notCompatibleClasses.add(oc.getNameOrOID());
2243      }
2244    }
2245    if (!notCompatibleClasses.isEmpty())
2246    {
2247      String arg = Utilities.getStringFromCollection(notCompatibleClasses, ", ");
2248      if (objectClassType == ObjectClassType.STRUCTURAL)
2249      {
2250        errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_STRUCTURAL.get(arg));
2251      }
2252      else if (objectClassType == ObjectClassType.AUXILIARY)
2253      {
2254        errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_AUXILIARY.get(arg));
2255      }
2256      else if (objectClassType == ObjectClassType.ABSTRACT)
2257      {
2258        errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_ABSTRACT.get(arg));
2259      }
2260    }
2261  }
2262}