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