/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2013-2015 ForgeRock AS. */ package org.opends.guitools.controlpanel.ui; import static org.opends.guitools.controlpanel.ui.ControlCenterMainPane.*; import static org.opends.messages.AdminToolMessages.*; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.naming.NamingEnumeration; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.swing.Box; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.border.Border; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import org.forgerock.i18n.LocalizableMessageDescriptor; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.schema.ObjectClassType; import org.opends.admin.ads.util.ConnectionUtils; import org.opends.guitools.controlpanel.browser.BrowserController; import org.opends.guitools.controlpanel.browser.IconPool; import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor; import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement; import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes; import org.opends.guitools.controlpanel.datamodel.ScheduleType; import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; import org.opends.guitools.controlpanel.datamodel.SortableListModel; import org.opends.guitools.controlpanel.event.ConfigChangeListener; import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; import org.opends.guitools.controlpanel.event.ConfigurationElementCreatedListener; import org.opends.guitools.controlpanel.task.RebuildIndexTask; import org.opends.guitools.controlpanel.task.RestartServerTask; import org.opends.guitools.controlpanel.task.StartServerTask; import org.opends.guitools.controlpanel.task.StopServerTask; import org.opends.guitools.controlpanel.task.Task; import org.opends.guitools.controlpanel.ui.components.AddRemovePanel; import org.opends.guitools.controlpanel.util.BackgroundTask; import org.opends.guitools.controlpanel.util.LowerCaseComparator; import org.opends.guitools.controlpanel.util.Utilities; import org.opends.quicksetup.ui.CustomHTMLEditorKit; import org.opends.server.schema.SchemaConstants; import org.opends.server.types.ObjectClass; import org.opends.server.types.OpenDsException; import org.opends.server.util.ServerConstants; import org.opends.server.util.StaticUtils; /** * An abstract class that contains a number of methods that are shared by all * the inheriting classes. In general a StatusGenericPanel is contained in a * GenericDialog and specifies the kind of buttons that this dialog has. The * StatusGenericPanel is also notified when the dialog is displayed (through the * toBeDisplayed method) */ public abstract class StatusGenericPanel extends JPanel implements ConfigChangeListener { private static final long serialVersionUID = -9123358652232556732L; /** * The string to be used as combo separator. */ public static final String COMBO_SEPARATOR = "----------"; /** * The not applicable message. */ protected static final LocalizableMessage NOT_APPLICABLE = INFO_NOT_APPLICABLE_LABEL.get(); private static final LocalizableMessage AUTHENTICATE = INFO_AUTHENTICATE_BUTTON_LABEL.get(); private static final LocalizableMessage START = INFO_START_BUTTON_LABEL.get(); private ControlPanelInfo info; private final boolean enableClose = true; private boolean enableCancel = true; private boolean enableOK = true; private boolean disposeOnClose; private final JPanel cardPanel; private final JPanel mainPanel; private final JEditorPane message; private final CardLayout cardLayout; private static final String MAIN_PANEL = "mainPanel"; private static final String MESSAGE_PANEL = "messagePanel"; private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** The error pane. */ protected JEditorPane errorPane; /** The last displayed message in the error pane. */ private String lastDisplayedError; private final List<ConfigurationElementCreatedListener> confListeners = new ArrayList<>(); private boolean sizeSet; private boolean focusSet; private static final DateFormat taskDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); /** * Returns the title that will be used as title of the dialog. * * @return the title that will be used as title of the dialog. */ public abstract LocalizableMessage getTitle(); /** * Returns the buttons that the dialog where this panel is contained should * display. * * @return the buttons that the dialog where this panel is contained should * display. */ public GenericDialog.ButtonType getButtonType() { return GenericDialog.ButtonType.OK_CANCEL; } /** * Returns the component that should get the focus when the dialog that * contains this panel is displayed. * * @return the component that should get the focus. */ public abstract Component getPreferredFocusComponent(); /** * Returns <CODE>true</CODE> if this panel requires some bordering (in general * an EmptyBorder with some insets) and <CODE>false</CODE> otherwise. * * @return <CODE>true</CODE> if this panel requires some bordering (in general * an EmptyBorder with some insets) and <CODE>false</CODE> otherwise. */ public boolean requiresBorder() { return true; } /** * Returns the menu bar that the panel might have. Returns <CODE>null</CODE> * if the panel has no menu bar associated. * * @return the menu bar that the panel might have. */ public JMenuBar getMenuBar() { return null; } /** * This method is called to indicate that the configuration changes should be * called in the background. In the case of panels which require some time to * be updated with the new configuration this method returns <CODE>true</CODE> * and the operation will be performed in the background while a message of * type 'Loading...' is displayed on the panel. * * @return <CODE>true</CODE> if changes should be loaded in the background and * <CODE>false</CODE> otherwise. */ public boolean callConfigurationChangedInBackground() { return false; } /** * The panel is notified that the dialog is going to be visible or invisible. * * @param visible * whether is going to be visible or not. */ public void toBeDisplayed(final boolean visible) { } /** * Tells whether this panel should be contained in a scroll pane or not. * * @return <CODE>true</CODE> if this panel should be contained in a scroll * pane and <CODE>false</CODE> otherwise. */ public boolean requiresScroll() { return true; } /** * Constructor. */ protected StatusGenericPanel() { super(new GridBagLayout()); setBackground(ColorAndFontConstants.background); cardLayout = new CardLayout(); cardPanel = new JPanel(cardLayout); cardPanel.setOpaque(false); mainPanel = new JPanel(new GridBagLayout()); mainPanel.setOpaque(false); message = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; gbc.weighty = 1.0; super.add(cardPanel, gbc); cardPanel.add(mainPanel, MAIN_PANEL); JPanel messagePanel = new JPanel(new GridBagLayout()); messagePanel.setOpaque(false); gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; messagePanel.add(message, gbc); cardPanel.add(messagePanel, MESSAGE_PANEL); cardLayout.show(cardPanel, MAIN_PANEL); } /** * The components are not added directly to the panel but to the main panel. * This is done to be able to display a message that takes the whole panel (of * type 'Loading...') when we are doing long operations. * * @param comp * the Component to be added. * @param constraints * the constraints. */ @Override public void add(final Component comp, final Object constraints) { mainPanel.add(comp, constraints); } /** * Adds a bottom glue to the main panel with the provided constraints. * * @param gbc * the constraints. */ protected void addBottomGlue(final GridBagConstraints gbc) { GridBagConstraints gbc2 = (GridBagConstraints) gbc.clone(); gbc2.insets = new Insets(0, 0, 0, 0); gbc2.gridy++; gbc2.gridwidth = GridBagConstraints.REMAINDER; gbc2.weighty = 1.0; gbc2.fill = GridBagConstraints.VERTICAL; add(Box.createVerticalGlue(), gbc2); gbc.gridy++; } /** * Returns a label with text 'Required Field' and an icon (used as legend in * some panels). * * @return a label with text 'Required Field' and an icon (used as legend in * some panels). */ protected JLabel createRequiredLabel() { JLabel requiredLabel = Utilities.createInlineHelpLabel(INFO_CTRL_PANEL_INDICATES_REQUIRED_FIELD_LABEL.get()); requiredLabel.setIcon(Utilities.createImageIcon(IconPool.IMAGE_PATH + "/required.gif")); return requiredLabel; } /** * Creates and adds an error pane. Is up to the caller to set the proper * gridheight, gridwidth, gridx and gridy on the provided GridBagConstraints. * * @param baseGbc * the GridBagConstraints to be used. */ protected void addErrorPane(final GridBagConstraints baseGbc) { addErrorPane(this, baseGbc); } /** * Adds an error pane to the provided container. Is up to the caller to set * the proper gridheight, gridwidth, gridx and gridy on the provided * GridBagConstraints. * * @param baseGbc * the GridBagConstraints to be used. * @param p * the container. */ protected void addErrorPane(final Container p, final GridBagConstraints baseGbc) { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = baseGbc.gridx; gbc.gridy = baseGbc.gridy; gbc.gridwidth = baseGbc.gridwidth; gbc.gridheight = baseGbc.gridheight; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.BOTH; if (requiresBorder()) { gbc.insets = new Insets(0, 0, 10, 0); } else { gbc.insets = new Insets(20, 20, 0, 20); } createErrorPane(); p.add(errorPane, gbc); } /** * Creates the error pane. */ protected void createErrorPane() { errorPane = Utilities.makeHtmlPane("", ColorAndFontConstants.progressFont); errorPane.setOpaque(false); errorPane.setEditable(false); errorPane.setVisible(false); CustomHTMLEditorKit htmlEditor = new CustomHTMLEditorKit(); htmlEditor.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent ev) { if (AUTHENTICATE.toString().equals(ev.getActionCommand())) { authenticate(); } else if (START.toString().equals(ev.getActionCommand())) { startServer(); } } }); errorPane.setEditorKit(htmlEditor); } /** * Commodity method used to add lines, where each line contains a label, a * component and an inline help label. * * @param labels * the labels. * @param comps * the components. * @param inlineHelp * the inline help labels. * @param panel * the panel where we will add the lines. * @param gbc * the grid bag constraints. */ protected void add(final JLabel[] labels, final Component[] comps, final JLabel[] inlineHelp, final Container panel, final GridBagConstraints gbc) { int i = 0; for (Component comp : comps) { gbc.insets.left = 0; gbc.weightx = 0.0; gbc.gridx = 0; if (labels[i] != null) { panel.add(labels[i], gbc); } gbc.insets.left = 10; gbc.weightx = 1.0; gbc.gridx = 1; panel.add(comp, gbc); if (inlineHelp[i] != null) { gbc.insets.top = 3; gbc.gridy++; panel.add(inlineHelp[i], gbc); } gbc.insets.top = 10; gbc.gridy++; i++; } } /** * Enables the OK button in the parent dialog. * * @param enable * whether to enable or disable the button. */ protected void setEnabledOK(final boolean enable) { Window w = Utilities.getParentDialog(this); if (w instanceof GenericDialog) { ((GenericDialog) w).setEnabledOK(enable); } else if (w instanceof GenericFrame) { ((GenericFrame) w).setEnabledOK(enable); } enableOK = enable; } /** * Enables the Cancel button in the parent dialog. * * @param enable * whether to enable or disable the button. */ protected void setEnabledCancel(final boolean enable) { Window w = Utilities.getParentDialog(this); if (w instanceof GenericDialog) { ((GenericDialog) w).setEnabledCancel(enable); } else if (w instanceof GenericFrame) { ((GenericFrame) w).setEnabledCancel(enable); } enableCancel = enable; } /** * Updates the font type and color of the component to be invalid and primary. * * @param comp * the component to update. */ protected void setPrimaryInvalid(final JComponent comp) { comp.setFont(ColorAndFontConstants.primaryInvalidFont); comp.setForeground(ColorAndFontConstants.invalidFontColor); } /** * Updates the font type and color of the component to be valid and primary. * * @param comp * the component to update. */ protected void setPrimaryValid(final JComponent comp) { comp.setForeground(ColorAndFontConstants.validFontColor); comp.setFont(ColorAndFontConstants.primaryFont); } /** * Updates the font type and color of the component to be invalid and * secondary. * * @param comp * the component to update. */ protected void setSecondaryInvalid(final JComponent comp) { comp.setForeground(ColorAndFontConstants.invalidFontColor); comp.setFont(ColorAndFontConstants.invalidFont); } /** * Updates the font type and color of the component to be valid and secondary. * * @param comp * the component to update. */ protected void setSecondaryValid(final JComponent comp) { comp.setForeground(ColorAndFontConstants.validFontColor); comp.setFont(ColorAndFontConstants.defaultFont); } /** * Packs the parent dialog. */ protected void packParentDialog() { Window dlg = Utilities.getParentDialog(this); if (dlg != null) { invalidate(); dlg.invalidate(); dlg.pack(); if (!SwingUtilities.isEventDispatchThread()) { Thread.dumpStack(); } } } /** * Notification that the ok button has been clicked, the panel is in charge of * doing whatever is required (close the dialog, launch a task, etc.). */ public abstract void okClicked(); /** * Adds a configuration element created listener. * * @param listener * the listener. */ public void addConfigurationElementCreatedListener(final ConfigurationElementCreatedListener listener) { getConfigurationElementCreatedListeners().add(listener); } /** * Removes a configuration element created listener. * * @param listener * the listener. */ public void removeConfigurationElementCreatedListener(final ConfigurationElementCreatedListener listener) { getConfigurationElementCreatedListeners().remove(listener); } /** * Returns the list of configuration listeners. * * @return the list of configuration listeners. */ protected List<ConfigurationElementCreatedListener> getConfigurationElementCreatedListeners() { return confListeners; } /** * Notification that cancel was clicked, the panel is in charge of doing * whatever is required (close the dialog, etc.). */ public void cancelClicked() { // Default implementation Utilities.getParentDialog(this).setVisible(false); if (isDisposeOnClose()) { Utilities.getParentDialog(this).dispose(); } } /** * Whether the dialog should be disposed when the user closes it. * * @return <CODE>true</CODE> if the dialog should be disposed when the user * closes it or <CODE>true</CODE> otherwise. */ public boolean isDisposeOnClose() { return disposeOnClose; } /** * Sets whether the dialog should be disposed when the user closes it or not. * * @param disposeOnClose * <CODE>true</CODE> if the dialog should be disposed when the user * closes it or <CODE>true</CODE> otherwise. */ public void setDisposeOnClose(final boolean disposeOnClose) { this.disposeOnClose = disposeOnClose; } /** * Notification that close was clicked, the panel is in charge of doing * whatever is required (close the dialog, etc.). */ public void closeClicked() { // Default implementation Utilities.getParentDialog(this).setVisible(false); if (isDisposeOnClose()) { Utilities.getParentDialog(this).dispose(); } } /** * Displays a dialog with the provided list of error messages. * * @param errors * the error messages. */ protected void displayErrorDialog(final Collection<LocalizableMessage> errors) { Utilities.displayErrorDialog(Utilities.getParentDialog(this), errors); } /** * Displays a confirmation message. * * @param title * the title/summary of the message. * @param msg * the description of the confirmation. * @return <CODE>true</CODE> if the user confirms and <CODE>false</CODE> * otherwise. */ protected boolean displayConfirmationDialog(final LocalizableMessage title, final LocalizableMessage msg) { return Utilities.displayConfirmationDialog(Utilities.getParentDialog(this), title, msg); } /** * If the index must be rebuilt, asks the user for confirmation. If the user * confirms launches a task that will rebuild the indexes. The progress will * be displayed in the provided progress dialog. * * @param index * the index. * @param progressDialog * the progress dialog. */ protected void rebuildIndexIfNecessary(final AbstractIndexDescriptor index, final ProgressDialog progressDialog) { progressDialog.setTaskIsOver(false); boolean rebuildIndexes; String backendName = index.getBackend().getBackendID(); LocalizableMessage summary = INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_SUMMARY.get(); if (!isServerRunning()) { rebuildIndexes = Utilities.displayConfirmationDialog( progressDialog, summary, INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_OFFLINE_DETAILS.get(index.getName(), backendName)); } else if (isLocal()) { rebuildIndexes = Utilities.displayConfirmationDialog(progressDialog, summary, INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_ONLINE_DETAILS.get(index.getName(), backendName, backendName)); } else { Utilities.displayWarningDialog(progressDialog, summary, INFO_CTRL_PANEL_INDEX_REBUILD_REQUIRED_REMOTE_DETAILS.get(index.getName(), backendName)); rebuildIndexes = false; } if (rebuildIndexes) { SortedSet<AbstractIndexDescriptor> indexes = new TreeSet<>(); indexes.add(index); SortedSet<String> baseDNs = new TreeSet<>(); for (BaseDNDescriptor b : index.getBackend().getBaseDns()) { baseDNs.add(Utilities.unescapeUtf8(b.getDn().toString())); } RebuildIndexTask newTask = new RebuildIndexTask(getInfo(), progressDialog, baseDNs, indexes); List<LocalizableMessage> errors = new ArrayList<>(); for (Task task : getInfo().getTasks()) { task.canLaunch(newTask, errors); } if (errors.isEmpty()) { progressDialog.appendProgressHtml("<br><br>"); launchOperation(newTask, INFO_CTRL_PANEL_REBUILDING_INDEXES_SUMMARY.get(backendName), INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_SUMMARY.get(), INFO_CTRL_PANEL_REBUILDING_INDEXES_SUCCESSFUL_DETAILS.get(), ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_SUMMARY.get(), null, ERR_CTRL_PANEL_REBUILDING_INDEXES_ERROR_DETAILS, progressDialog, false); if (progressDialog.isModal()) { progressDialog.toFront(); } progressDialog.setVisible(true); if (!progressDialog.isModal()) { progressDialog.toFront(); } } if (!errors.isEmpty()) { displayErrorDialog(errors); } } else { progressDialog.setTaskIsOver(true); if (progressDialog.isVisible()) { progressDialog.toFront(); } } } /** * A class used to avoid the possibility a certain type of objects in a combo * box. This is used for instance in the combo box that contains base DNs * where the base DNs are separated in backends, so the combo box displays * both the backends (~ categories) and base DNs (~ values) and we do not * allow to select the backends (~ categories). */ protected class IgnoreItemListener implements ItemListener { private Object selectedItem; private final JComboBox combo; /** * Constructor. * * @param combo * the combo box. */ public IgnoreItemListener(final JComboBox combo) { this.combo = combo; selectedItem = combo.getSelectedItem(); if (isCategory(selectedItem)) { selectedItem = null; } } @Override public void itemStateChanged(final ItemEvent ev) { Object o = combo.getSelectedItem(); if (isCategory(o)) { if (selectedItem == null) { selectedItem = firstNonCategoryItem(combo.getModel()); } if (selectedItem != null) { combo.setSelectedItem(selectedItem); } } else if (COMBO_SEPARATOR.equals(o)) { combo.setSelectedItem(selectedItem); } else { selectedItem = o; } } private Object firstNonCategoryItem(ComboBoxModel model) { for (int i = 0; i < model.getSize(); i++) { Object item = model.getElementAt(i); if (item instanceof CategorizedComboBoxElement && !isCategory(item)) { return item; } } return null; } } /** * Returns the HTML required to render an Authenticate button in HTML. * * @return the HTML required to render an Authenticate button in HTML. */ protected String getAuthenticateHTML() { return "<INPUT type=\"submit\" value=\"" + AUTHENTICATE + "\"></INPUT>"; } /** * Returns the HTML required to render an Start button in HTML. * * @return the HTML required to render an Start button in HTML. */ protected String getStartServerHTML() { return "<INPUT type=\"submit\" value=\"" + START + "\"></INPUT>"; } /** * Updates the error panel and enables/disables the OK button depending on the * status of the server. * * @param desc * the Server Descriptor. * @param details * the message to be displayed if authentication has not been * provided and the server is running. */ protected void updateErrorPaneAndOKButtonIfAuthRequired( final ServerDescriptor desc, final LocalizableMessage details) { if (authenticationRequired(desc)) { LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(details); mb.append("<br><br>").append(getAuthenticateHTML()); LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get(); updateErrorPane( errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(true); packParentDialog(); setEnabledOK(false); } }); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(false); checkOKButtonEnable(); } }); } } /** * Returns <CODE>true</CODE> if the server is running and the user did not * provide authentication and <CODE>false</CODE> otherwise. * * @param desc * the server descriptor. * @return <CODE>true</CODE> if the server is running and the user did not * provide authentication and <CODE>false</CODE> otherwise. */ protected boolean authenticationRequired(final ServerDescriptor desc) { ServerDescriptor.ServerStatus status = desc.getStatus(); return (status == ServerDescriptor.ServerStatus.STARTED && !desc.isAuthenticated()) || status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE; } /** * Updates the error panel depending on the status of the server. * * @param desc * the Server Descriptor. * @param details * the message to be displayed if authentication has not been * provided and the server is running. */ protected void updateErrorPaneIfAuthRequired(final ServerDescriptor desc, final LocalizableMessage details) { if (authenticationRequired(desc)) { LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get(); LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(details); mb.append("<br><br>").append(getAuthenticateHTML()); updateErrorPane(errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(true); packParentDialog(); } }); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(false); } }); } } /** * Updates the error panel depending on the status of the server. This method * will display an error message in the error pane if the server is not * running and another message if the server is running but authentication has * not been provided. * * @param desc * the Server Descriptor. * @param detailsServerNotRunning * the message to be displayed if the server is not running. * @param authRequired * the message to be displayed if authentication has not been * provided and the server is running. */ protected void updateErrorPaneIfServerRunningAndAuthRequired(final ServerDescriptor desc, final LocalizableMessage detailsServerNotRunning, final LocalizableMessage authRequired) { ServerDescriptor.ServerStatus status = desc.getStatus(); if (status != ServerDescriptor.ServerStatus.STARTED && status != ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE) { LocalizableMessage title = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get(); LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(detailsServerNotRunning); mb.append("<br><br>").append(getStartServerHTML()); updateErrorPane( errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); SwingUtilities.invokeLater(new Runnable() { /** {@inheritDoc} */ @Override public void run() { errorPane.setVisible(true); packParentDialog(); } }); } else if (authenticationRequired(desc)) { LocalizableMessage title = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get(); LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(authRequired); mb.append("<br><br>").append(getAuthenticateHTML()); updateErrorPane( errorPane, title, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(true); packParentDialog(); } }); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { errorPane.setVisible(false); } }); } } /** * Updates the enabling/disabling of the OK button. The code assumes that the * error pane has already been updated. */ protected void checkOKButtonEnable() { setEnabledOK(!errorPane.isVisible()); } /** * Returns <CODE>true</CODE> if the provided object is a category object in a * combo box. * * @param o * the item in the combo box. * @return <CODE>true</CODE> if the provided object is a category object in a * combo box. */ protected boolean isCategory(final Object o) { if (o instanceof CategorizedComboBoxElement) { CategorizedComboBoxElement desc = (CategorizedComboBoxElement) o; return desc.getType() == CategorizedComboBoxElement.Type.CATEGORY; } return false; } /** * Returns the control panel info object. * * @return the control panel info object. */ public ControlPanelInfo getInfo() { return info; } /** * Sets the control panel info object. * * @param info * the control panel info object. */ public void setInfo(final ControlPanelInfo info) { if (!info.equals(this.info)) { if (this.info != null) { this.info.removeConfigChangeListener(this); } this.info = info; this.info.addConfigChangeListener(this); if (SwingUtilities.isEventDispatchThread() && callConfigurationChangedInBackground()) { final Color savedBackground = getBackground(); setBackground(ColorAndFontConstants.background); if (!sizeSet) { setPreferredSize(mainPanel.getPreferredSize()); sizeSet = true; } // Do it outside the event thread if the panel requires it. BackgroundTask<Void> worker = new BackgroundTask<Void>() { @Override public Void processBackgroundTask() throws Throwable { StaticUtils.sleep(1000); configurationChanged(new ConfigurationChangeEvent(StatusGenericPanel.this.info, StatusGenericPanel.this.info.getServerDescriptor())); return null; } @Override public void backgroundTaskCompleted(final Void returnValue, final Throwable t) { setBackground(savedBackground); displayMainPanel(); if (!focusSet) { focusSet = true; Component comp = getPreferredFocusComponent(); if (comp != null) { comp.requestFocusInWindow(); } } } }; displayMessage(INFO_CTRL_PANEL_LOADING_PANEL_SUMMARY.get()); worker.startBackgroundTask(); } else if (info.getServerDescriptor() != null) { configurationChanged(new ConfigurationChangeEvent(this.info, this.info.getServerDescriptor())); } } } /** Displays the main panel. */ protected void displayMainPanel() { cardLayout.show(cardPanel, MAIN_PANEL); } /** * Displays a message and hides the main panel. * * @param msg * the message to be displayed. */ protected void displayMessage(final LocalizableMessage msg) { message.setText(Utilities.applyFont(msg.toString(), ColorAndFontConstants.defaultFont)); cardLayout.show(cardPanel, MESSAGE_PANEL); message.requestFocusInWindow(); } /** * Displays an error message and hides the main panel. * * @param title * the title of the message to be displayed. * @param msg * the message to be displayed. */ protected void displayErrorMessage(final LocalizableMessage title, final LocalizableMessage msg) { updateErrorPane(message, title, ColorAndFontConstants.errorTitleFont, msg, ColorAndFontConstants.defaultFont); cardLayout.show(cardPanel, MESSAGE_PANEL); message.requestFocusInWindow(); } /** * Updates the contents of an editor pane using the error format. * * @param pane * the editor pane to be updated. * @param title * the title. * @param titleFont * the font to be used for the title. * @param details * the details message. * @param detailsFont * the font to be used for the details. */ protected void updateErrorPane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont, final LocalizableMessage details, final Font detailsFont) { updatePane(pane, title, titleFont, details, detailsFont, PanelType.ERROR); } /** * Updates the contents of an editor pane using the confirmation format. * * @param pane * the editor pane to be updated. * @param title * the title. * @param titleFont * the font to be used for the title. * @param details * the details message. * @param detailsFont * the font to be used for the details. */ protected void updateConfirmationPane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont, final LocalizableMessage details, final Font detailsFont) { updatePane(pane, title, titleFont, details, detailsFont, PanelType.CONFIRMATION); } /** The different types of error panels that are handled. */ private enum PanelType { /** The message in the panel is an error. */ ERROR, /** The message in the panel is a confirmation. */ CONFIRMATION, /** The message in the panel is an information message. */ INFORMATION, /** The message in the panel is a warning message. */ WARNING } /** * Updates the contents of an editor pane using the provided format. * * @param pane * the editor pane to be updated. * @param title * the title. * @param titleFont * the font to be used for the title. * @param details * the details message. * @param detailsFont * the font to be used for the details. * @param type * the type of panel. */ private void updatePane(final JEditorPane pane, final LocalizableMessage title, final Font titleFont, final LocalizableMessage details, final Font detailsFont, final PanelType type) { String text = getText(type, title, titleFont, details, detailsFont); if (!text.equals(lastDisplayedError)) { LocalizableMessage wrappedTitle = Utilities.wrapHTML(title, 80); LocalizableMessage wrappedDetails = Utilities.wrapHTML(details, 90); JEditorPane wrappedPane = Utilities.makeHtmlPane(null, pane.getFont()); String wrappedText; switch (type) { case ERROR: wrappedText = Utilities.getFormattedError(wrappedTitle, titleFont, wrappedDetails, detailsFont); break; default: wrappedText = Utilities.getFormattedSuccess(wrappedTitle, titleFont, wrappedDetails, detailsFont); break; } wrappedPane.setText(wrappedText); Dimension d = wrappedPane.getPreferredSize(); pane.setText(text); pane.setPreferredSize(d); lastDisplayedError = text; } final Window window = Utilities.getParentDialog(StatusGenericPanel.this); if (window != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { pane.invalidate(); window.validate(); } }); } } private String getText( PanelType type, LocalizableMessage title, Font titleFont, LocalizableMessage details, Font detailsFont) { switch (type) { case ERROR: return Utilities.getFormattedError(title, titleFont, details, detailsFont); case CONFIRMATION: return Utilities.getFormattedConfirmation(title, titleFont, details, detailsFont); case WARNING: return Utilities.getFormattedWarning(title, titleFont, details, detailsFont); default: return Utilities.getFormattedSuccess(title, titleFont, details, detailsFont); } } /** * Commodity method used to update the elements of a combo box that contains * the different user backends. If no backends are found the combo box will be * made invisible and a label will be made visible. This method does not * update the label's text nor creates any layout. * * @param combo * the combo to be updated. * @param lNoBackendsFound * the label that must be shown if no user backends are found. * @param desc * the server descriptor that contains the configuration. */ protected void updateSimpleBackendComboBoxModel(final JComboBox combo, final JLabel lNoBackendsFound, final ServerDescriptor desc) { final SortedSet<String> newElements = new TreeSet<>(new LowerCaseComparator()); for (BackendDescriptor backend : desc.getBackends()) { if (!backend.isConfigBackend()) { newElements.add(backend.getBackendID()); } } DefaultComboBoxModel model = (DefaultComboBoxModel) combo.getModel(); updateComboBoxModel(newElements, model); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { boolean noElems = newElements.isEmpty(); combo.setVisible(!noElems); lNoBackendsFound.setVisible(noElems); } }); } /** * Method that says if a backend must be displayed. Only non-config backends * are displayed. * * @param backend * the backend. * @return <CODE>true</CODE> if the backend must be displayed and * <CODE>false</CODE> otherwise. */ protected boolean displayBackend(final BackendDescriptor backend) { return !backend.isConfigBackend(); } /** * Commodity method to update a combo box model with the backends of a server. * * @param model * the combo box model to be updated. * @param desc * the server descriptor containing the configuration. */ protected void updateBaseDNComboBoxModel(final DefaultComboBoxModel model, final ServerDescriptor desc) { Set<CategorizedComboBoxElement> newElements = new LinkedHashSet<>(); SortedSet<String> backendIDs = new TreeSet<>(new LowerCaseComparator()); Map<String, SortedSet<String>> hmBaseDNs = new HashMap<>(); for (BackendDescriptor backend : desc.getBackends()) { if (displayBackend(backend)) { String backendID = backend.getBackendID(); backendIDs.add(backendID); SortedSet<String> baseDNs = new TreeSet<>(new LowerCaseComparator()); for (BaseDNDescriptor baseDN : backend.getBaseDns()) { try { baseDNs.add(Utilities.unescapeUtf8(baseDN.getDn().toString())); } catch (Throwable t) { throw new RuntimeException("Unexpected error: " + t, t); } } hmBaseDNs.put(backendID, baseDNs); } } for (String backendID : backendIDs) { newElements.add(new CategorizedComboBoxElement(backendID, CategorizedComboBoxElement.Type.CATEGORY)); SortedSet<String> baseDNs = hmBaseDNs.get(backendID); for (String baseDN : baseDNs) { newElements.add(new CategorizedComboBoxElement(baseDN, CategorizedComboBoxElement.Type.REGULAR)); } } updateComboBoxModel(newElements, model); } /** * Updates a combo box model with a number of items. * * @param newElements * the new items for the combo box model. * @param model * the combo box model to be updated. */ protected void updateComboBoxModel(final Collection<?> newElements, final DefaultComboBoxModel model) { updateComboBoxModel(newElements, model, null); } /** * Updates a combo box model with a number of items. The method assumes that * is called outside the event thread. * * @param newElements * the new items for the combo box model. * @param model * the combo box model to be updated. * @param comparator * the object that will be used to compare the objects in the model. * If <CODE>null</CODE>, the equals method will be used. */ private void updateComboBoxModel(final Collection<?> newElements, final DefaultComboBoxModel model, final Comparator<Object> comparator) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Utilities.updateComboBoxModel(newElements, model, comparator); } }); } /** * Updates a map, so that the keys are the base DN where the indexes are * defined and the values are a sorted set of indexes. * * @param desc * the server descriptor containing the index configuration. * @param hmIndexes * the map to be updated. */ protected void updateIndexMap( final ServerDescriptor desc, final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes) { synchronized (hmIndexes) { Set<String> dns = new HashSet<>(); for (BackendDescriptor backend : desc.getBackends()) { if (backend.getType() == BackendDescriptor.Type.PLUGGABLE) { for (BaseDNDescriptor baseDN : backend.getBaseDns()) { String dn; try { dn = Utilities.unescapeUtf8(baseDN.getDn().toString()); } catch (Throwable t) { throw new RuntimeException("Unexpected error: " + t, t); } dns.add(dn); SortedSet<AbstractIndexDescriptor> indexes = new TreeSet<AbstractIndexDescriptor>(backend.getIndexes()); indexes.addAll(backend.getVLVIndexes()); SortedSet<AbstractIndexDescriptor> currentIndexes = hmIndexes.get(dn); if (currentIndexes != null) { if (!currentIndexes.equals(indexes)) { hmIndexes.put(dn, indexes); } } else { hmIndexes.put(dn, indexes); } } } } for (String dn : new HashSet<String>(hmIndexes.keySet())) { if (!dns.contains(dn)) { hmIndexes.remove(dn); } } } } /** * Updates and addremove panel with the contents of the provided item. The * selected item represents a base DN. * * @param hmIndexes * the map that contains the indexes definitions as values and the * base DNs as keys. * @param selectedItem * the selected item. * @param addRemove * the add remove panel to be updated. */ protected void comboBoxSelected(final Map<String, SortedSet<AbstractIndexDescriptor>> hmIndexes, final CategorizedComboBoxElement selectedItem, final AddRemovePanel<AbstractIndexDescriptor> addRemove) { synchronized (hmIndexes) { String selectedDn = null; if (selectedItem != null) { selectedDn = (String) selectedItem.getValue(); } if (selectedDn != null) { SortedSet<AbstractIndexDescriptor> indexes = hmIndexes.get(selectedDn); if (indexes != null) { boolean availableChanged = false; boolean selectedChanged = false; SortableListModel<AbstractIndexDescriptor> availableListModel = addRemove.getAvailableListModel(); SortableListModel<AbstractIndexDescriptor> selectedListModel = addRemove.getSelectedListModel(); SortedSet<AbstractIndexDescriptor> availableIndexes = availableListModel.getData(); SortedSet<AbstractIndexDescriptor> selectedIndexes = selectedListModel.getData(); availableChanged = availableIndexes.retainAll(indexes); selectedChanged = selectedIndexes.retainAll(indexes); for (AbstractIndexDescriptor index : indexes) { if (!availableIndexes.contains(index) && !selectedIndexes.contains(index)) { availableIndexes.add(index); availableChanged = true; } } if (availableChanged) { availableListModel.clear(); availableListModel.addAll(availableIndexes); availableListModel.fireContentsChanged(availableListModel, 0, availableListModel.getSize()); } if (selectedChanged) { selectedListModel.clear(); selectedListModel.addAll(selectedIndexes); selectedListModel.fireContentsChanged(selectedListModel, 0, selectedListModel.getSize()); } } } } } /** * Returns <CODE>true</CODE> if the cancel button is enabled and * <CODE>false</CODE> otherwise. * * @return <CODE>true</CODE> if the cancel button is enabled and * <CODE>false</CODE> otherwise. */ public boolean isEnableCancel() { return enableCancel; } /** * Returns <CODE>true</CODE> if the close button is enabled and * <CODE>false</CODE> otherwise. * * @return <CODE>true</CODE> if the close button is enabled and * <CODE>false</CODE> otherwise. */ public boolean isEnableClose() { return enableClose; } /** * Returns <CODE>true</CODE> if the ok button is enabled and * <CODE>false</CODE> otherwise. * * @return <CODE>true</CODE> if the ok button is enabled and * <CODE>false</CODE> otherwise. */ public boolean isEnableOK() { return enableOK; } /** * Returns <CODE>true</CODE> if the server is running and <CODE>false</CODE> * otherwise. * * @return <CODE>true</CODE> if the server is running and <CODE>false</CODE> * otherwise. */ protected boolean isServerRunning() { return getInfo().getServerDescriptor().getStatus() == ServerDescriptor.ServerStatus.STARTED; } /** * Returns <CODE>true</CODE> if the managed server is the local installation * (where the control panel is installed) <CODE>false</CODE> otherwise. * * @return <CODE>true</CODE> if the managed server is the local installation * (where the control panel is installed) <CODE>false</CODE> * otherwise. */ protected boolean isLocal() { return getInfo().getServerDescriptor().isLocal(); } /** * Launch an task. * * @param task * the task to be launched. * @param initialSummary * the initial summary to be displayed in the progress dialog. * @param successSummary * the success summary to be displayed in the progress dialog if the * task is successful. * @param successDetail * the success details to be displayed in the progress dialog if the * task is successful. * @param errorSummary * the error summary to be displayed in the progress dialog if the * task ended with error. * @param errorDetail * error details to be displayed in the progress dialog if the task * ended with error. * @param errorDetailCode * error detail message to be displayed in the progress dialog if the * task ended with error and we have an exit error code (for instance * if the error occurred when launching a script we will have an * error code). * @param dialog * the progress dialog. */ protected void launchOperation(final Task task, final LocalizableMessage initialSummary, final LocalizableMessage successSummary, final LocalizableMessage successDetail, final LocalizableMessage errorSummary, final LocalizableMessage errorDetail, final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog) { launchOperation(task, initialSummary, successSummary, successDetail, errorSummary, errorDetail, errorDetailCode, dialog, true); } /** * Launch an task. * * @param task * the task to be launched. * @param initialSummary * the initial summary to be displayed in the progress dialog. * @param successSummary * the success summary to be displayed in the progress dialog if the * task is successful. * @param successDetail * the success details to be displayed in the progress dialog if the * task is successful. * @param errorSummary * the error summary to be displayed in the progress dialog if the * task ended with error. * @param errorDetail * error details to be displayed in the progress dialog if the task * ended with error. * @param errorDetailCode * error detail message to be displayed in the progress dialog if the * task ended with error and we have an exit error code (for instance * if the error occurred when launching a script we will have an * error code). * @param dialog * the progress dialog. * @param resetLogs * whether the contents of the progress dialog should be reset or * not. */ private void launchOperation(final Task task, final LocalizableMessage initialSummary, final LocalizableMessage successSummary, final LocalizableMessage successDetail, final LocalizableMessage errorSummary, final LocalizableMessage errorDetail, final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog, final boolean resetLogs) { launchOperation(task, initialSummary, successSummary, successDetail, errorSummary, errorDetail, errorDetailCode, dialog, resetLogs, getInfo()); } /** * Launch an task. * * @param task * the task to be launched. * @param initialSummary * the initial summary to be displayed in the progress dialog. * @param successSummary * the success summary to be displayed in the progress dialog if the * task is successful. * @param successDetail * the success details to be displayed in the progress dialog if the * task is successful. * @param errorSummary * the error summary to be displayed in the progress dialog if the * task ended with error. * @param errorDetail * error details to be displayed in the progress dialog if the task * ended with error. * @param errorDetailCode * error detail message to be displayed in the progress dialog if the * task ended with error and we have an exit error code (for instance * if the error occurred when launching a script we will have an * error code). * @param dialog * the progress dialog. * @param resetLogs * whether the contents of the progress dialog should be reset or * not. * @param info * the ControlPanelInfo. */ public static void launchOperation(final Task task, final LocalizableMessage initialSummary, final LocalizableMessage successSummary, final LocalizableMessage successDetail, final LocalizableMessage errorSummary, final LocalizableMessage errorDetail, final LocalizableMessageDescriptor.Arg1<Number> errorDetailCode, final ProgressDialog dialog, final boolean resetLogs, final ControlPanelInfo info) { dialog.setTaskIsOver(false); dialog.getProgressBar().setIndeterminate(true); dialog.addPrintStreamListeners(task.getOutPrintStream(), task.getErrorPrintStream()); if (resetLogs) { dialog.resetProgressLogs(); } String cmdLine = task.getCommandLineToDisplay(); if (cmdLine != null) { dialog.appendProgressHtml(Utilities.applyFont(INFO_CTRL_PANEL_EQUIVALENT_COMMAND_LINE.get() + "<br><b>" + cmdLine + "</b><br><br>", ColorAndFontConstants.progressFont)); } dialog.setEnabledClose(false); dialog.setSummary(LocalizableMessage.raw(Utilities.applyFont(initialSummary.toString(), ColorAndFontConstants.defaultFont))); dialog.getProgressBar().setVisible(true); BackgroundTask<Task> worker = new BackgroundTask<Task>() { @Override public Task processBackgroundTask() throws Throwable { task.runTask(); if (task.regenerateDescriptor()) { info.regenerateDescriptor(); } return task; } @Override public void backgroundTaskCompleted(final Task returnValue, Throwable t) { String summaryMsg; if (task.getState() == Task.State.FINISHED_SUCCESSFULLY) { summaryMsg = Utilities.getFormattedSuccess(successSummary, ColorAndFontConstants.errorTitleFont, successDetail, ColorAndFontConstants.defaultFont); } else { if (t == null) { t = task.getLastException(); } if (t != null) { logger.warn(LocalizableMessage.raw("Error occurred running task: " + t, t)); if (task.getReturnCode() != null && errorDetailCode != null) { String sThrowable; if (t instanceof OpenDsException) { sThrowable = ((OpenDsException) t).getMessageObject().toString(); } else if (t.getMessage() != null) { sThrowable = t.getMessage(); } else { sThrowable = t.toString(); } LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(errorDetailCode.get(task.getReturnCode())); mb.append(" ").append(INFO_CTRL_PANEL_DETAILS_THROWABLE.get(sThrowable)); summaryMsg = Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); } else if (errorDetail != null) { LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); mb.append(errorDetail); mb.append(INFO_CTRL_PANEL_DETAILS_THROWABLE.get(t)); summaryMsg = Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, mb.toMessage(), ColorAndFontConstants.defaultFont); } else { summaryMsg = null; } } else if (task.getReturnCode() != null && errorDetailCode != null) { summaryMsg = Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, errorDetailCode .get(task.getReturnCode()), ColorAndFontConstants.defaultFont); } else if (errorDetail != null) { summaryMsg = Utilities.getFormattedError(errorSummary, ColorAndFontConstants.errorTitleFont, errorDetail, ColorAndFontConstants.defaultFont); } else { summaryMsg = null; } } if (summaryMsg != null) { dialog.setSummary(LocalizableMessage.raw(summaryMsg)); } dialog.setEnabledClose(true); dialog.getProgressBar().setVisible(false); if (task.getState() == Task.State.FINISHED_SUCCESSFULLY) { dialog.setTaskIsOver(true); } task.postOperation(); } }; info.registerTask(task); worker.startBackgroundTask(); } /** * Checks that the provided string value is a valid integer and if it is not * updates a list of error messages with an error. * * @param errors * the list of error messages to be updated. * @param stringValue * the string value to analyze. * @param minValue * the minimum integer value accepted. * @param maxValue * the maximum integer value accepted. * @param errMsg * the error message to use to update the error list if the provided * value is not valid. * @return {@code true} if the provided string value is a valid integer and if * it is not updates a list of error messages with an error. */ protected boolean checkIntValue(final Collection<LocalizableMessage> errors, final String stringValue, final int minValue, final int maxValue, final LocalizableMessage errMsg) { try { int n = Integer.parseInt(stringValue); if (minValue <= n && n <= maxValue) { return true; } } catch (NumberFormatException ignored) { } errors.add(errMsg); return false; } /** * Starts the server. This method will launch a task and open a progress * dialog that will start the server. This method must be called from the * event thread. */ protected void startServer() { Set<LocalizableMessage> errors = new LinkedHashSet<>(); ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this), INFO_CTRL_PANEL_START_SERVER_PROGRESS_DLG_TITLE.get(), getInfo()); StartServerTask newTask = new StartServerTask(getInfo(), progressDialog); for (Task task : getInfo().getTasks()) { task.canLaunch(newTask, errors); } if (errors.isEmpty()) { launchOperation(newTask, INFO_CTRL_PANEL_STARTING_SERVER_SUMMARY.get(), INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_SUMMARY.get(), INFO_CTRL_PANEL_STARTING_SERVER_SUCCESSFUL_DETAILS.get(), ERR_CTRL_PANEL_STARTING_SERVER_ERROR_SUMMARY.get(), null, ERR_CTRL_PANEL_STARTING_SERVER_ERROR_DETAILS, progressDialog); progressDialog.setVisible(true); } else { displayErrorDialog(errors); } } /** * Stops the server. This method will launch a task and open a progress dialog * that will stop the server. This method must be called from the event * thread. */ protected void stopServer() { Set<LocalizableMessage> errors = new LinkedHashSet<>(); ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this), INFO_CTRL_PANEL_STOP_SERVER_PROGRESS_DLG_TITLE.get(), getInfo()); StopServerTask newTask = new StopServerTask(getInfo(), progressDialog); for (Task task : getInfo().getTasks()) { task.canLaunch(newTask, errors); } boolean confirmed = true; if (errors.isEmpty()) { confirmed = displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(), INFO_CTRL_PANEL_CONFIRM_STOP_SERVER_DETAILS.get()); } if (errors.isEmpty() && confirmed) { launchOperation(newTask, INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(), INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_SUMMARY.get(), INFO_CTRL_PANEL_STOPPING_SERVER_SUCCESSFUL_DETAILS.get(), ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_SUMMARY.get(), null, ERR_CTRL_PANEL_STOPPING_SERVER_ERROR_DETAILS, progressDialog); progressDialog.setVisible(true); } if (!errors.isEmpty()) { displayErrorDialog(errors); } } /** * Restarts the server. This method will launch a task and open a progress * dialog that will restart the server. This method must be called from the * event thread. */ protected void restartServer() { Set<LocalizableMessage> errors = new LinkedHashSet<>(); ProgressDialog progressDialog = new ProgressDialog(Utilities.createFrame(), Utilities.getParentDialog(this), INFO_CTRL_PANEL_RESTART_SERVER_PROGRESS_DLG_TITLE.get(), getInfo()); RestartServerTask newTask = new RestartServerTask(getInfo(), progressDialog); for (Task task : getInfo().getTasks()) { task.canLaunch(newTask, errors); } boolean confirmed = true; if (errors.isEmpty()) { confirmed = displayConfirmationDialog(INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(), INFO_CTRL_PANEL_CONFIRM_RESTART_SERVER_DETAILS.get()); } if (errors.isEmpty() && confirmed) { launchOperation(newTask, INFO_CTRL_PANEL_STOPPING_SERVER_SUMMARY.get(), INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_SUMMARY.get(), INFO_CTRL_PANEL_RESTARTING_SERVER_SUCCESSFUL_DETAILS.get(), ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_SUMMARY.get(), null, ERR_CTRL_PANEL_RESTARTING_SERVER_ERROR_DETAILS, progressDialog); progressDialog.setVisible(true); } if (!errors.isEmpty()) { displayErrorDialog(errors); } } /** * Displays a dialog asking for authentication. This method must be called * from the event thread. */ private void authenticate() { if (!getLoginDialog().isVisible()) { getLoginDialog().setVisible(true); } getLoginDialog().toFront(); } /** * Returns the login dialog that is displayed when the method authenticate is * called. * * @return the login dialog that is displayed when the method authenticate is * called. */ protected GenericDialog getLoginDialog() { GenericDialog dialog = isLocal() ? getLocalServerLoginDialog(getInfo()) : getLocalOrRemoteDialog(getInfo()); Utilities.centerGoldenMean(dialog, Utilities.getFrame(this)); dialog.setModal(true); return dialog; } /** * Tells whether an entry exists or not. Actually it tells if we could find a * given entry or not. * * @param dn * the DN of the entry to look for. * @return <CODE>true</CODE> if the entry with the provided DN could be found * and <CODE>false</CODE> otherwise. */ protected boolean entryExists(final String dn) { boolean entryExists = false; try { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES }); String filter = BrowserController.ALL_OBJECTS_FILTER; NamingEnumeration<SearchResult> result = getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls); try { while (result.hasMore()) { SearchResult sr = result.next(); entryExists = sr != null; } } finally { result.close(); } } catch (Throwable t) { } return entryExists; } /** * Tells whether a given entry exists and contains one of the specified object * classes. * * @param dn * the DN of the entry. * @param objectClasses * the object classes to check. * @return <CODE>true</CODE> if the entry exists and contains one of the * specified object classes and <CODE>false</CODE> otherwise. */ protected boolean hasObjectClass(final String dn, final String... objectClasses) { try { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes(new String[] { ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME }); String filter = BrowserController.ALL_OBJECTS_FILTER; NamingEnumeration<SearchResult> result = getInfo().getDirContext().search(Utilities.getJNDIName(dn), filter, ctls); try { while (result.hasMore()) { SearchResult sr = result.next(); Set<String> values = ConnectionUtils.getValues(sr, ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME); if (values != null) { for (String s : values) { for (String objectClass : objectClasses) { if (s.equalsIgnoreCase(objectClass)) { return true; } } } } } } finally { result.close(); } } catch (Throwable t) { } return false; } /** * Returns the border to be used in the right panel of the dialog with a tree * on the left (for instance the schema browser, entry browser and index * browser). * * @return the border to be used in the right panel. */ protected Border getRightPanelBorder() { return ColorAndFontConstants.textAreaBorder; } /** * Returns the monitoring value in a String form to be displayed to the user. * * @param attr * the attribute to analyze. * @param monitoringEntry * the monitoring entry. * @return the monitoring value in a String form to be displayed to the user. */ public static String getMonitoringValue(final MonitoringAttributes attr, final CustomSearchResult monitoringEntry) { return Utilities.getMonitoringValue(attr, monitoringEntry); } /** * Updates the monitoring information writing it to a list of labels. * * @param monitoringAttrs * the monitoring operations whose information we want to update. * @param monitoringLabels * the monitoring labels to be updated. * @param monitoringEntry * the monitoring entry containing the information to be displayed. */ protected void updateMonitoringInfo(final List<MonitoringAttributes> monitoringAttrs, final List<JLabel> monitoringLabels, final CustomSearchResult monitoringEntry) { for (int i = 0; i < monitoringAttrs.size(); i++) { String value = getMonitoringValue(monitoringAttrs.get(i), monitoringEntry); JLabel l = monitoringLabels.get(i); l.setText(value); } } /** * Returns the label to be used in panels (with ':') based on the definition * of the monitoring attribute. * * @param attr * the monitoring attribute. * @return the label to be used in panels (with ':') based on the definition * of the monitoring attribute. */ protected static LocalizableMessage getLabel(final MonitoringAttributes attr) { return INFO_CTRL_PANEL_OPERATION_NAME_AS_LABEL.get(attr.getMessage()); } /** * Returns the command-line arguments associated with the provided schedule. * * @param schedule * the schedule. * @return the command-line arguments associated with the provided schedule. */ protected List<String> getScheduleArgs(final ScheduleType schedule) { List<String> args = new ArrayList<>(2); switch (schedule.getType()) { case LAUNCH_LATER: args.add("--start"); args.add(getStartTimeForTask(schedule.getLaunchLaterDate())); break; case LAUNCH_PERIODICALLY: args.add("--recurringTask"); args.add(schedule.getCronValue()); break; } return args; } /** * Checks whether the server is running or not and depending on the schedule * updates the list of errors with the errors found. * * @param schedule * the schedule. * @param errors * the list of errors. * @param label * the label to be marked as invalid if errors where encountered. */ protected void addScheduleErrors(final ScheduleType schedule, final Collection<LocalizableMessage> errors, final JLabel label) { if (!isServerRunning()) { ScheduleType.Type type = schedule.getType(); if (type == ScheduleType.Type.LAUNCH_LATER) { errors.add(ERR_CTRL_PANEL_LAUNCH_LATER_REQUIRES_SERVER_RUNNING.get()); setPrimaryInvalid(label); } else if (type == ScheduleType.Type.LAUNCH_PERIODICALLY) { errors.add(ERR_CTRL_PANEL_LAUNCH_SCHEDULE_REQUIRES_SERVER_RUNNING.get()); setPrimaryInvalid(label); } } } private String getStartTimeForTask(final Date date) { return taskDateFormat.format(date); } /** * Checks whether the provided superior object classes are compatible with the * provided object class type. If not, the method updates the provided list of * error messages with a message describing the incompatibility. * * @param objectClassSuperiors * the superior object classes. * @param objectClassType * the object class type. * @param errors * the list of error messages. */ protected void checkCompatibleSuperiors(final Set<ObjectClass> objectClassSuperiors, final ObjectClassType objectClassType, final List<LocalizableMessage> errors) { SortedSet<String> notCompatibleClasses = new TreeSet<>(new LowerCaseComparator()); for (ObjectClass oc : objectClassSuperiors) { if (oc.getObjectClassType() == ObjectClassType.ABSTRACT) { // Nothing to do. } else if (oc.getObjectClassType() != objectClassType) { notCompatibleClasses.add(oc.getNameOrOID()); } } if (!notCompatibleClasses.isEmpty()) { String arg = Utilities.getStringFromCollection(notCompatibleClasses, ", "); if (objectClassType == ObjectClassType.STRUCTURAL) { errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_STRUCTURAL.get(arg)); } else if (objectClassType == ObjectClassType.AUXILIARY) { errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_AUXILIARY.get(arg)); } else if (objectClassType == ObjectClassType.ABSTRACT) { errors.add(ERR_CTRL_PANEL_INCOMPATIBLE_SUPERIORS_WITH_ABSTRACT.get(arg)); } } } }