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