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